Railway Operation Simulator  v2.23.1
A railway simulator for Windows
InterfaceUnit.cpp
Go to the documentation of this file.
1 // InterfaceUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 
27 #include <Classes.hpp>
28 #include <Controls.hpp>
29 #include <StdCtrls.hpp>
30 #include <Forms.hpp>
31 #include <Buttons.hpp>
32 #include <ExtCtrls.hpp>
33 #include <Menus.hpp>
34 #include <Dialogs.hpp>
35 #include <Graphics.hpp>
36 #include <ComCtrls.hpp>
37 #include <Clipbrd.hpp> //for selection clipboard functions at v2.8.0
38 #include <fstream>
39 #include <sstream> //for clipboard functions at v2.8.0
40 #include <vector>
41 #include <vcl.h>
42 #include <stdio.h>
43 #include <algorithm> //for sort
44 
45 #pragma hdrstop
46 // The above batch of include files above #pragma hdrstop appear in all .cpp files.
47 // They aren't all needed in each case but being together and identical they speed
48 // up compilation considerably (without ~14 min, with ~1 min!). They are used in
49 // conjunction with 'use pre-compiled headers' in the project compiler options.
50 
51 #include "InterfaceUnit.h"
52 #include "GraphicUnit.h"
53 //#include "DisplayUnit.h" included in TrackUnit.h
54 #include "TextUnit.h"
55 #include "TrainUnit.h"
56 #include "TrackUnit.h"
57 #include "AboutUnit.h"
58 #include "PerfLogUnit.h" //added at v2.13.0 for Performance log form
59 #include "ActionsDueUnit.h" //added at v2.13.0 for ActionsDue form
60 #include "Utilities.h"
61 #include "API.h" //added at v2.10.0
62 #include <dirent.h>
63 #include <Filectrl.hpp> //to check whether directories exist
64 
65 // ---------------------------------------------------------------------------
66 #include <Vcl.HTMLHelpViewer.hpp> //added at v2.0.0 for access to the .chm help file
67 #pragma package(smart_init)
68 #pragma link "Vcl.HTMLHelpViewer" //added at v2.0.0 for access to the .chm help file
69 #pragma resource "*.dfm"
70 
72 API *session_api_; //moved from header to avoid AboutForm having access and defining _session_api_
73  //as well as Interface and giving a warning, added at v2.10.0
74 
75 // Folder Names
76 const UnicodeString TInterface::RAILWAY_DIR_NAME = "Railways";
77 const UnicodeString TInterface::TIMETABLE_DIR_NAME = "Program timetables";
78 const UnicodeString TInterface::PERFLOG_DIR_NAME = "Performance logs";
79 const UnicodeString TInterface::SESSION_DIR_NAME = "Sessions";
80 const UnicodeString TInterface::IMAGE_DIR_NAME = "Images";
81 const UnicodeString TInterface::FORMATTEDTT_DIR_NAME = "Formatted timetables";
82 const UnicodeString TInterface::USERGRAPHICS_DIR_NAME = "Graphics";
83 
84 // ---------------------------------------------------------------------------
85 
86 __fastcall TInterface::TInterface(TComponent* Owner) : TForm(Owner)
87 {
88  // constructor
89  try
90  {
91 // TestFunctionCount = 0; //used only in test function
92 // TestFunctionFirstPass = true; //used only in test function
93 
94 
95  Screen->Cursor = TCursor(-11); // Hourglass
96  DirOpenError = false;
97  AllSetUpFlag = false; //flag to prevent MasterClock from being enabled when application activates if there has been an error during
98  // initial setup
99  // MasterClock->Enabled = false;//keep this stopped until all set up (no effect here as form not yet created, made false in object insp)
100  // Visible = false; //keep the Interface form invisible until all set up (no effect here as form not yet created, made false in object insp)
101  ProgramVersion = "RailOS32" + GetVersion();
102  // use GNU Major/Minor/Patch version numbering system, change for each published modification, Dev x = interim internal
103  // development stages (don't show on published versions)
104 
105  // check for presence of directories, creation failure probably indicates that the
106  // working folder is read-only
107 
108  CurDir = AnsiString(GetCurrentDir());
109 // ShowMessage("Curdir from GetCurrentDir() " + CurDir); //these used to check behaviour outside the compiler
110  UnicodeString FullProgramName = GetModuleName(0); // added at v2.9.0 to check executable exists
111 // ShowMessage("FullProgramName " + FullProgramName);
112  UnicodeString ProgramName = ExtractFileName(FullProgramName); // as above
113 // ShowMessage("ProgramName " + ProgramName);
114  UnicodeString ProgramDirectoryName = ExtractFilePath(FullProgramName); // as above
115 // ShowMessage("ProgramDirectoryName " + ProgramDirectoryName);
116 
117  if(!FileExists(ProgramName)) //added at v2.9.0 after discovering the effect described in the message
118  {
119  if(!SetCurrentDir(ProgramDirectoryName)) //if false the current directory couldn't be changed
120  {
121  ShowMessage("The working directory does not contain the railway executable file so the program cannot "
122  "open. This is usually because the program has been selected via the right-click taskbar icon though it may "
123  "also happen in other circumstances. It is caused by the Windows operating system re-assigning the "
124  "working directory for some unknown reason, though whether or not it happens appears to depend on the "
125  "Windows update version.\n\n"
126  "To avoid this happening please open the program by double clicking the program icon on the desktop "
127  "if there is one, or the program icon shown in Windows Explorer.");
128  Application->Terminate();
129  }
130  else
131  {
132  CurDir = AnsiString(GetCurrentDir());
133  }
134  }
135 
136  if(!DirectoryExists(RAILWAY_DIR_NAME))
137  {
138  if(!CreateDir(RAILWAY_DIR_NAME))
139  {
140  DirOpenError = true;
141  }
142 
143  }
144  if(!DirectoryExists(TIMETABLE_DIR_NAME))
145  {
146  if(!CreateDir(TIMETABLE_DIR_NAME))
147  {
148  DirOpenError = true;
149  }
150  }
151  if(!DirectoryExists(PERFLOG_DIR_NAME))
152  {
153  if(!CreateDir(PERFLOG_DIR_NAME))
154  {
155  DirOpenError = true;
156  }
157  }
158  if(!DirectoryExists(SESSION_DIR_NAME))
159  {
160  if(!CreateDir(SESSION_DIR_NAME))
161  {
162  DirOpenError = true;
163  }
164  }
165  if(!DirectoryExists(IMAGE_DIR_NAME))
166  {
167  if(!CreateDir(IMAGE_DIR_NAME))
168  {
169  DirOpenError = true;
170  }
171  }
172  if(!DirectoryExists(FORMATTEDTT_DIR_NAME))
173  {
174  if(!CreateDir(FORMATTEDTT_DIR_NAME))
175  {
176  DirOpenError = true;
177  }
178  }
179  if(!DirectoryExists(USERGRAPHICS_DIR_NAME))
180  {
181  if(!CreateDir(USERGRAPHICS_DIR_NAME))
182  {
183  DirOpenError = true;
184  }
185  }
186  if(DirOpenError)
187  {
188  ShowMessage("Failed to create one or more of folders: " + RAILWAY_DIR_NAME + ", " + TIMETABLE_DIR_NAME + ", " + PERFLOG_DIR_NAME + ", " +
190  "program operation will be restricted");
191  }
192  Application->HelpFile = AnsiString(CurDir + "\\Help.chm"); // added at v2.0.0 for .chm help file
193 
194  MainMenu1->AutoHotkeys = maManual; // Embarcadero mod: to suppress '&' inclusion for underlined characters in menu items
195  PopupMenu->AutoHotkeys = maManual; // as above
196 
197  Utilities = new TUtilities;
198  RailGraphics = new TRailGraphics();
199 
200  int DispW = (Screen->Width - 64) / 16; // will truncate down to a multiple of 16 OK here as screen dimensions are accurate
201  int DispH = (Screen->Height - 192) / 16; // Interface dimensions are 16 too wide & 14 short in height
202  MainScreen->Width = DispW * 16; //already created on form
203  MainScreen->Height = DispH * 16;
204 
207  Utilities->ScreenElementWidth = DispW;
209  HiddenScreen = new TImage(Interface);
210  HiddenScreen->Width = MainScreen->Width;
211  HiddenScreen->Height = MainScreen->Height;
214  StaticFeaturesScreen = new TImage(Interface);
215  StaticFeaturesScreen->Width = MainScreen->Width;
216  StaticFeaturesScreen->Height = MainScreen->Height;
220  Track = new TTrack;
221  AllRoutes = new TAllRoutes;
227  SelectBitmap = new Graphics::TBitmap;
228  SelectBitmap->PixelFormat = pf8bit;
229  SelectBitmap->Transparent = true;
234  LengthWarningSentFlag = false;
235  PasteWarningSentFlag = false; // added at v2.6.0
236  FillSelectionMessageSentFlag = false; // added at v2.6.0
237  LCManualLowerBarriersMessageSent = false; // added at v2.6.0
238  RecoverClipboardMessageSent = false; // added at v2.8.0
239  TooLongMessageSentFlag = false; //added at v2.9.1
240  TooShortMessageSentFlag = false; //added at v2.9.1
241  Utilities->NoPlatsMessageSent = false; //added at v2.10.0
242  PrefDirConflictAdviceMessageSent = false; //added at v2.13.0
243  TrainLeaveWarningSent = false; //added at v2.14.0
244  InvertTTEntryMessageSent = false; //added at v2.15.0
245  AZWarningSent = false; //added at v2.15.1
246  TimeWarningSent = false; //added at v2.18.0
247  TTAZSaveWarningNotRequired = false; //added at v2.15.1
248  TTTimeSaveWarningNotRequired = false; //added at v2.18.0
249  LengthHeatmapBitBtn->Visible = false;
250  SpeedHeatmapBitBtn->Visible = false;
251  HeatmapsRedlowvaluesMenuItem->Enabled = false;
252  DistanceKey->Visible = false;
254  LengthHeatMapImage->Visible = false;
256  SpeedHeatMapImage->Visible = false;
257  Utilities->RedLowFlag = true;
258  HeatmapsRedlowvaluesMenuItem->Caption = "Heatmaps: Set Red to Represent High Values";
259  Utilities->DefaultTrackLength = 100; //moved here at v2.11.0, may be changed in reading config.txt //changed at v2.13.1
260  Utilities->DefaultTrackSpeedLimit = 200; //moved here at v2.11.0, may be changed in reading config.txt
261 
262  TrackInfoShowing = false;
263  TrainStatusShowing = true;
264  TrainTTShowing = true;
265  //these menu items rearranged at v2.22.0
266  TrackInfoOnOffMenuItem->Caption = "Show Track &Information"; // added here at v1.2.0 because dropped from ResetAll()
267  TrainStatusInfoOnOffMenuItem->Caption = "Hide Train &Status Information"; // changed at v2.0.0 so normally visible
268  TrainTTInfoOnOffMenuItem->Caption = "Hide Train &Timetable Information"; // as above
269  TrainLongServRefInfoOnOffMenuItem->Caption = "Show &Long Service References";
270  NoDelaysMenuItem->Enabled = false;
271  NoFailuresMenuItem->Enabled = false;
272 
273  /* ======================= ROS Dummy API ===============================
274  Connect API to track variables of interest, added at v2.10.0
275  */
276  session_api_ = new API(CurDir + "\\session.ini");
279  session_api_->add_metadata_str("performance_file", &PerformanceFileName);
281  session_api_->add_metadata_int("operation_mode", &api_oper_mode_);
282  session_api_->write_string("program_version", GetVersion()); // API v1.2
283 
284  // =====================================================================
285 
286  ResetAll(0);
287 
288  TempTTFileName = "";
289 
294 
295  RouteFlashDuration = 0.0;
296  PointsFlashDuration = 0.0;
297 
298  FloatingLabel->Color = clB4G5R5;
299  TrackElementPanel->Color = clB5G5R4;
300  InfoPanel->Color = clB4G5R5;
301 
302  Utilities->RHSignalFlag = false; // new at v2.3.0 for RH signals, always left hand on startup
303  SigsOnLeftImage1->Picture->Bitmap->LoadFromResourceName(0, "SigsOnLeft");
304  SigsOnLeftImage2->Picture->Bitmap->LoadFromResourceName(0, "SigsOnLeft");
305  SigsOnLeftImage1->Transparent = true;
306  SigsOnLeftImage2->Transparent = true;
307  SigsOnLeftImage1->Picture->Bitmap->TransparentColor = clB5G5R5;
308  SigsOnLeftImage2->Picture->Bitmap->TransparentColor = clB5G5R5;
309  SigsOnRightImage1->Picture->Bitmap->LoadFromResourceName(0, "SigsOnRight");
310  SigsOnRightImage2->Picture->Bitmap->LoadFromResourceName(0, "SigsOnRight");
311  SigsOnRightImage1->Transparent = true;
312  SigsOnRightImage2->Transparent = true;
313  SigsOnRightImage1->Picture->Bitmap->TransparentColor = clB5G5R5;
314  SigsOnRightImage2->Picture->Bitmap->TransparentColor = clB5G5R5;
315 
316  SaveRailwayDialog->InitialDir = CurDir + "\\" + RAILWAY_DIR_NAME; // default locations, may be changed when Config.txt loaded
317  LoadRailwayDialog->InitialDir = CurDir + "\\" + RAILWAY_DIR_NAME;
318  TimetableDialog->InitialDir = CurDir + "\\" + TIMETABLE_DIR_NAME;
319  SaveTTDialog->InitialDir = CurDir + "\\" + TIMETABLE_DIR_NAME;
320  LoadSessionDialog->InitialDir = CurDir + "\\" + SESSION_DIR_NAME;
321  LoadUserGraphicDialog->InitialDir = CurDir + "\\" + USERGRAPHICS_DIR_NAME; // not changeable
322  LoadCouplingFileDialog->InitialDir = CurDir; // not changeable
323  ReloadConfigMenuItem->Enabled = true; //new at v2.11.0
324  bool NoConfig = false;
325  LoadConfigFile(0, true, NoConfig); //true for first load
326  if(NoConfig)
327  {
328  SaveConfigFile(0); //added at v2.13.1 so always have a config file
329  }
330 
331  SpeedButton1->Glyph->LoadFromResourceName(0, "gl1");
332  SpeedButton2->Glyph->LoadFromResourceName(0, "gl2");
333  SpeedButton3->Glyph->LoadFromResourceName(0, "gl3");
334  SpeedButton4->Glyph->LoadFromResourceName(0, "gl4");
335  SpeedButton5->Glyph->LoadFromResourceName(0, "gl5");
336  SpeedButton6->Glyph->LoadFromResourceName(0, "gl6");
337  SpeedButton7->Glyph->LoadFromResourceName(0, "gl7");
338  SpeedButton8->Glyph->LoadFromResourceName(0, "gl8");
339  SpeedButton9->Glyph->LoadFromResourceName(0, "gl9");
340  SpeedButton10->Glyph->LoadFromResourceName(0, "gl10");
341  SpeedButton11->Glyph->LoadFromResourceName(0, "gl11");
342  SpeedButton12->Glyph->LoadFromResourceName(0, "gl12");
343  SpeedButton13->Glyph->LoadFromResourceName(0, "gl13");
344  SpeedButton14->Glyph->LoadFromResourceName(0, "gl14");
345  SpeedButton15->Glyph->LoadFromResourceName(0, "gl15");
346  SpeedButton16->Glyph->LoadFromResourceName(0, "gl16");
347  SpeedButton18->Glyph->LoadFromResourceName(0, "gl18");
348  SpeedButton19->Glyph->LoadFromResourceName(0, "gl19");
349  SpeedButton20->Glyph->LoadFromResourceName(0, "gl20");
350  SpeedButton21->Glyph->LoadFromResourceName(0, "gl21");
351  SpeedButton22->Glyph->LoadFromResourceName(0, "gl22");
352  SpeedButton23->Glyph->LoadFromResourceName(0, "gl23");
353  SpeedButton24->Glyph->LoadFromResourceName(0, "gl24");
354  SpeedButton25->Glyph->LoadFromResourceName(0, "gl25");
355  SpeedButton26->Glyph->LoadFromResourceName(0, "gl26");
356  SpeedButton27->Glyph->LoadFromResourceName(0, "gl27");
357  SpeedButton28->Glyph->LoadFromResourceName(0, "gl28");
358  SpeedButton29->Glyph->LoadFromResourceName(0, "gl29");
359  SpeedButton30->Glyph->LoadFromResourceName(0, "gl30");
360  SpeedButton31->Glyph->LoadFromResourceName(0, "gl31");
361  SpeedButton32->Glyph->LoadFromResourceName(0, "gl32");
362  SpeedButton33->Glyph->LoadFromResourceName(0, "gl33");
363  SpeedButton34->Glyph->LoadFromResourceName(0, "gl34");
364  SpeedButton35->Glyph->LoadFromResourceName(0, "gl35");
365  SpeedButton36->Glyph->LoadFromResourceName(0, "gl36");
366  SpeedButton37->Glyph->LoadFromResourceName(0, "gl37");
367  SpeedButton38->Glyph->LoadFromResourceName(0, "gl38");
368  SpeedButton39->Glyph->LoadFromResourceName(0, "gl39");
369  SpeedButton40->Glyph->LoadFromResourceName(0, "gl40");
370  SpeedButton41->Glyph->LoadFromResourceName(0, "gl41");
371  SpeedButton42->Glyph->LoadFromResourceName(0, "gl42");
372  SpeedButton43->Glyph->LoadFromResourceName(0, "gl43");
373  SpeedButton44->Glyph->LoadFromResourceName(0, "gl44");
374  SpeedButton45->Glyph->LoadFromResourceName(0, "gl45");
375  SpeedButton46->Glyph->LoadFromResourceName(0, "gl46");
376  SpeedButton47->Glyph->LoadFromResourceName(0, "gl47");
377  SpeedButton48->Glyph->LoadFromResourceName(0, "gl48");
378  SpeedButton49->Glyph->LoadFromResourceName(0, "gl49");
379  SpeedButton50->Glyph->LoadFromResourceName(0, "gl50");
380  SpeedButton51->Glyph->LoadFromResourceName(0, "gl51");
381  SpeedButton52->Glyph->LoadFromResourceName(0, "gl52");
382  SpeedButton53->Glyph->LoadFromResourceName(0, "gl53");
383  SpeedButton54->Glyph->LoadFromResourceName(0, "gl54");
384  SpeedButton55->Glyph->LoadFromResourceName(0, "gl55");
385  SpeedButton56->Glyph->LoadFromResourceName(0, "gl56");
386  SpeedButton57->Glyph->LoadFromResourceName(0, "gl57");
387  SpeedButton58->Glyph->LoadFromResourceName(0, "gl58");
388  SpeedButton59->Glyph->LoadFromResourceName(0, "gl59");
389  SpeedButton60->Glyph->LoadFromResourceName(0, "gl60");
390  SpeedButton61->Glyph->LoadFromResourceName(0, "gl61");
391  SpeedButton62->Glyph->LoadFromResourceName(0, "gl62");
392  SpeedButton63->Glyph->LoadFromResourceName(0, "gl63");
393  SpeedButton64->Glyph->LoadFromResourceName(0, "gl64");
394  SpeedButton65->Glyph->LoadFromResourceName(0, "gl65");
395  SpeedButton66->Glyph->LoadFromResourceName(0, "gl66");
396  SpeedButton67->Glyph->LoadFromResourceName(0, "gl67");
397  SpeedButton68->Glyph->LoadFromResourceName(0, "gl68");
398  SpeedButton69->Glyph->LoadFromResourceName(0, "gl69");
399  SpeedButton70->Glyph->LoadFromResourceName(0, "gl70");
400  SpeedButton71->Glyph->LoadFromResourceName(0, "gl71");
401  SpeedButton72->Glyph->LoadFromResourceName(0, "gl72");
402  SpeedButton73->Glyph->LoadFromResourceName(0, "gl73");
403  SpeedButton74->Glyph->LoadFromResourceName(0, "gl74");
404  SpeedButton75->Glyph->LoadFromResourceName(0, "gl75");
405  SpeedButton76->Glyph->LoadFromResourceName(0, "gl76");
406  SpeedButton77->Glyph->LoadFromResourceName(0, "gl77");
407  SpeedButton78->Glyph->LoadFromResourceName(0, "gl78");
408  SpeedButton79->Glyph->LoadFromResourceName(0, "gl79");
409  SpeedButton80->Glyph->LoadFromResourceName(0, "gl80");
410  SpeedButton81->Glyph->LoadFromResourceName(0, "gl81");
411  SpeedButton82->Glyph->LoadFromResourceName(0, "gl82");
412  SpeedButton83->Glyph->LoadFromResourceName(0, "gl83");
413  SpeedButton84->Glyph->LoadFromResourceName(0, "gl84");
414  SpeedButton85->Glyph->LoadFromResourceName(0, "gl85");
415  SpeedButton86->Glyph->LoadFromResourceName(0, "gl86");
416  SpeedButton87->Glyph->LoadFromResourceName(0, "gl87");
417  SpeedButton88->Glyph->LoadFromResourceName(0, "gl88set");
418  SpeedButton89->Glyph->LoadFromResourceName(0, "gl89set");
419  SpeedButton90->Glyph->LoadFromResourceName(0, "gl90set");
420  SpeedButton91->Glyph->LoadFromResourceName(0, "gl91set");
421  SpeedButton92->Glyph->LoadFromResourceName(0, "gl92set");
422  SpeedButton93->Glyph->LoadFromResourceName(0, "gl93set");
423  SpeedButton94->Glyph->LoadFromResourceName(0, "gl94set");
424  SpeedButton95->Glyph->LoadFromResourceName(0, "gl95set");
425  SpeedButton96->Glyph->LoadFromResourceName(0, "ConcourseGlyph");
426  SpeedButton97->Glyph->LoadFromResourceName(0, "gl97");
427  SpeedButton98->Glyph->LoadFromResourceName(0, "gl98");
428  SpeedButton99->Glyph->LoadFromResourceName(0, "gl99");
429  SpeedButton100->Glyph->LoadFromResourceName(0, "gl100");
430  SpeedButton101->Glyph->LoadFromResourceName(0, "gl101");
431  SpeedButton102->Glyph->LoadFromResourceName(0, "gl102");
432  SpeedButton103->Glyph->LoadFromResourceName(0, "gl103");
433  SpeedButton104->Glyph->LoadFromResourceName(0, "gl104");
434  SpeedButton105->Glyph->LoadFromResourceName(0, "gl105");
435  SpeedButton106->Glyph->LoadFromResourceName(0, "gl106");
436  SpeedButton107->Glyph->LoadFromResourceName(0, "gl107");
437  SpeedButton108->Glyph->LoadFromResourceName(0, "gl108");
438  SpeedButton109->Glyph->LoadFromResourceName(0, "gl109");
439  SpeedButton110->Glyph->LoadFromResourceName(0, "gl110");
440  SpeedButton111->Glyph->LoadFromResourceName(0, "gl111");
441  SpeedButton112->Glyph->LoadFromResourceName(0, "gl112");
442  SpeedButton113->Glyph->LoadFromResourceName(0, "gl113");
443  SpeedButton114->Glyph->LoadFromResourceName(0, "gl114");
444  SpeedButton115->Glyph->LoadFromResourceName(0, "gl115");
445  SpeedButton116->Glyph->LoadFromResourceName(0, "gl116");
446  SpeedButton117->Glyph->LoadFromResourceName(0, "gl117");
447  SpeedButton118->Glyph->LoadFromResourceName(0, "gl118");
448  SpeedButton119->Glyph->LoadFromResourceName(0, "gl119");
449  SpeedButton120->Glyph->LoadFromResourceName(0, "gl120");
450  SpeedButton121->Glyph->LoadFromResourceName(0, "gl121");
451  SpeedButton122->Glyph->LoadFromResourceName(0, "gl122");
452  SpeedButton123->Glyph->LoadFromResourceName(0, "gl123");
453  SpeedButton124->Glyph->LoadFromResourceName(0, "gl124");
454  SpeedButton125->Glyph->LoadFromResourceName(0, "gl125");
455  SpeedButton126->Glyph->LoadFromResourceName(0, "gl126");
456  SpeedButton127->Glyph->LoadFromResourceName(0, "gl127");
457  SpeedButton128->Glyph->LoadFromResourceName(0, "gl128");
458  SpeedButton129->Glyph->LoadFromResourceName(0, "gl129");
459  SpeedButton130->Glyph->LoadFromResourceName(0, "gl130");
460  SpeedButton131->Glyph->LoadFromResourceName(0, "gl131");
461  SpeedButton132->Glyph->LoadFromResourceName(0, "gl132");
462  SpeedButton133->Glyph->LoadFromResourceName(0, "gl133");
463  SpeedButton134->Glyph->LoadFromResourceName(0, "gl134");
464  SpeedButton135->Glyph->LoadFromResourceName(0, "gl135");
465  SpeedButton136->Glyph->LoadFromResourceName(0, "gl136");
466  SpeedButton137->Glyph->LoadFromResourceName(0, "gl137");
467  SpeedButton138->Glyph->LoadFromResourceName(0, "gl138");
468  SpeedButton139->Glyph->LoadFromResourceName(0, "gl139");
469  SpeedButton140->Glyph->LoadFromResourceName(0, "gl140");
470  SpeedButton141->Glyph->LoadFromResourceName(0, "gl141");
471  SpeedButton142->Glyph->LoadFromResourceName(0, "gl142");
472  SpeedButton143->Glyph->LoadFromResourceName(0, "gl143");
473  SpeedButton145->Glyph->LoadFromResourceName(0, "gl145");
474  SpeedButton146->Glyph->LoadFromResourceName(0, "gl146");
475  // below not in RailGraphics
476  SpeedButton144->Glyph->LoadFromResourceName(0, "LCGlyph");
477 
478  AddPrefDirButton->Glyph->LoadFromResourceName(0, "AddPrefDir");
479  AddTextButton->Glyph->LoadFromResourceName(0, "AddText");
480  AddTrackButton->Glyph->LoadFromResourceName(0, "AddTrack");
481  AutoSigsButton->Glyph->LoadFromResourceName(0, "AutoTop");
482  SigAutoNonConsecButton->Glyph->LoadFromResourceName(0, "AutoBottom");
483  CallingOnButton->Glyph->LoadFromResourceName(0, "CallingOn");
484  DeleteAllPrefDirButton->Glyph->LoadFromResourceName(0, "ClearAllPrefDir");
485  DeleteOnePrefDirButton->Glyph->LoadFromResourceName(0, "ClearOnePrefDir");
486  ExitOperationButton->Glyph->LoadFromResourceName(0, "Exit");
487  ExitPrefDirButton->Glyph->LoadFromResourceName(0, "Exit");
488  ExitTrackButton->Glyph->LoadFromResourceName(0, "Exit");
489  ExitTTModeButton->Glyph->LoadFromResourceName(0, "Exit");
490  FlashControlButton->Glyph->LoadFromResourceName(0, "NoFlash"); //added at v2.15.0
491  FontButton->Glyph->LoadFromResourceName(0, "FontGraphic");
492  HomeButton->Glyph->LoadFromResourceName(0, "Home");
493  LocationNameButton->Glyph->LoadFromResourceName(0, "NameLocs");
494  MoveTextOrGraphicButton->Glyph->LoadFromResourceName(0, "MoveTextOrGraphic");
495  NewHomeButton->Glyph->LoadFromResourceName(0, "NewHome");
496  UnrestrictedButton->Glyph->LoadFromResourceName(0, "NonSig");
497  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
498  OperatorActionButton->Glyph->LoadFromResourceName(0, "ShowOpActionPanel");
499  PerformanceLogButton->Glyph->LoadFromResourceName(0, "ShowLog");
500  PresetAutoSigRoutesButton->Glyph->LoadFromResourceName(0, "PresetAutoSigRoutes");
501  RouteCancelButton->Glyph->LoadFromResourceName(0, "RouteCancel");
502  SaveRailwayPDPButton->Glyph->LoadFromResourceName(0, "SaveRailway"); // PrefDirPanel
503  SaveRailwayBaseModeButton->Glyph->LoadFromResourceName(0, "SaveRailway"); // OperatingPanel
504  SaveRailwayTBPButton->Glyph->LoadFromResourceName(0, "SaveRailway"); // TrackBuildPanel
505  SaveSessionButton->Glyph->LoadFromResourceName(0, "SaveSession");
506  ScreenDownButton->Glyph->LoadFromResourceName(0, "BlackArrowDown");
507  ScreenGridButton->Glyph->LoadFromResourceName(0, "ScreenGrid");
508  ScreenLeftButton->Glyph->LoadFromResourceName(0, "BlackArrowLeft");
509  ScreenRightButton->Glyph->LoadFromResourceName(0, "BlackArrowRight");
510  ScreenUpButton->Glyph->LoadFromResourceName(0, "BlackArrowUp");
511  SetGapsButton->Glyph->LoadFromResourceName(0, "ConnectGaps");
512  SetLengthsButton->Glyph->LoadFromResourceName(0, "SetDists");
513  ShowHideTTButton->Glyph->LoadFromResourceName(0, "Hide");
514  SigAspectButton->Glyph->LoadFromResourceName(0, "FourAspect"); // new at version 0.6
515  SigPrefConsecButton->Glyph->LoadFromResourceName(0, "PrefTop");
516  SigPrefNonConsecButton->Glyph->LoadFromResourceName(0, "PrefBottom");
517  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision1");
518  TrackOKButton->Glyph->LoadFromResourceName(0, "Validate");
519  TTClockAdjButton->Glyph->LoadFromResourceName(0, "TTClock");
520  UserGraphicButton->Glyph->LoadFromResourceName(0, "PictureImage");
521 
522  BufferAttentionImage->Picture->Bitmap->LoadFromResourceName(0, "BufferWarning");
523  CallOnImage->Picture->Bitmap->LoadFromResourceName(0, "CallingOn");
524  CrashImage->Picture->Bitmap->LoadFromResourceName(0, "CrashWarning");
525  DerailImage->Picture->Bitmap->LoadFromResourceName(0, "DerailWarning");
526  SignalStopImage->Picture->Bitmap->LoadFromResourceName(0, "SignalStopWarning");
527  SPADImage->Picture->Bitmap->LoadFromResourceName(0, "SPADWarning");
528  TrainFailedImage->Picture->Bitmap->LoadFromResourceName(0, "TrainFailedWarning"); // new at v2.4.0
529  ManualLCDownImage->Picture->Bitmap->LoadFromResourceName(0, "ManualLCDownImage"); // new at v2.9.0
530 
531  DistanceKey->Picture->Bitmap->LoadFromResourceName(0, "DistanceKey");
532  LengthHeatMapImageRedLow->Picture->Bitmap->LoadFromResourceName(0, "LengthHeatMapImageRedLow");
533  LengthHeatMapImageRedHigh->Picture->Bitmap->LoadFromResourceName(0, "LengthHeatMapImageRedHigh");
534  SpeedHeatMapImageRedLow->Picture->Bitmap->LoadFromResourceName(0, "SpeedHeatMapImageRedLow");
535  SpeedHeatMapImageRedHigh->Picture->Bitmap->LoadFromResourceName(0, "SpeedHeatMapImageRedHigh");
536  PrefDirKey->Picture->Bitmap->LoadFromResourceName(0, "PrefDirKey");
537 
538  TrackLinkedImage->Picture->Bitmap->LoadFromResourceName(0, "TrackLinkedGraphic");
539  TrackNotLinkedImage->Picture->Bitmap->LoadFromResourceName(0, "TrackNotLinkedGraphic");
540  GapsNotSetImage->Picture->Bitmap->LoadFromResourceName(0, "GapsNotSetGraphic");
541  GapsSetImage->Picture->Bitmap->LoadFromResourceName(0, "GapsSetGraphic");
542  LocationNamesNotSetImage->Picture->Bitmap->LoadFromResourceName(0, "LocNamesNotSetGraphic");
543  LocationNamesSetImage->Picture->Bitmap->LoadFromResourceName(0, "LocNamesSetGraphic");
544 
545  SkipListExitImage->Picture->Bitmap->LoadFromResourceName(0, "Exit"); //new at v2.11.0
546  ReminderExitImage->Picture->Bitmap->LoadFromResourceName(0, "Exit"); //new at v2.19.0
547 
548 
549 /* Don't need this - load icon directly into both Interface form & Application (via Project - Options - Application - Load Icon)
550  RailwayIcon = new TPicture;
551  RailwayIcon->Icon->LoadFromResourceName(0, "Icon1.ico");
552  Icon = RailwayIcon->Icon;
553  Application->Icon = RailwayIcon->Icon;
554 */
555 
556  AnsiString NL = '\n';
557  const AnsiString TTLabelStr1 = "Start new train" + NL + "Start new service from a split" + NL + "Start new service from another service" + NL +
558  "Start new non-repeating shuttle finish service" + NL + "Start new shuttle train at a timetabled stop" + NL +
559  "Start new shuttle service from a feeder";
560 
561  const AnsiString TTLabelStr2 = "Pass" + NL + "Be joined by another train" + NL + "Front split" + NL + "Rear split" + NL + "Change direction of train" +
562  NL + "Change service description" + NL + "Change service maximum speed";
563 
564  const AnsiString TTLabelStr3 = "Finish && form a new service" + NL + "Finish && join another train" + NL + "Finish && exit railway" + NL +
565  "Finish && repeat shuttle, finally remain here" + NL + "Finish && repeat shuttle, finally form a finishing service" + NL +
566  "Finish non-repeating shuttle feeder service" + NL + "Finish && remain here";
567 
568  const AnsiString TTLabelStr4 = "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL +
569  "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + "HH:MM" + NL + " " +
570  NL + "R";
571 
572  const AnsiString TTLabelStr5 = "HH:MM ';' Location" + NL + "HH:MM ';' HH:MM ';' Location";
573 
574  const AnsiString TTLabelStr6 = "+ rear element ID - space - front element ID [+ optional ';S']" + NL + "+ ref. of the train that splits" + NL +
575  "+ other service ref." + NL + "+ shuttle service ref." + NL + "+ rear element ID - space - front element ID ';' linked shuttle ref." + NL +
576  "+ linked shuttle service ref. ';' feeder service ref." + NL + "+ location" + NL + "+ joining train ref." + NL + "+ new service ref. + [opt. "
577  "new service Mass%-Power% split]" + NL + "+ new service ref. + [opt. new service Mass%-Power% split]" + NL + " " + NL +
578  "+ new description" + NL + "+ new maximum speed" + NL + "+ new service ref." + NL + "+ ref. of train to join" + NL +
579  "+ list of valid exit element IDs (at least 1) separated by spaces" + NL + "+ linked shuttle service ref.";
580 
581  const AnsiString TTLabelStr7 = "Arrival OR departure time (program will identify) ';' location [+ optional ';min dwell time in seconds' (arrival only)]" + NL +
582  "Arrival time ';' departure time (with no events between) ';' location [+ optional ';min dwell time in seconds']";
583 
584  const AnsiString TTLabelStr9 = "Timetable entries" + NL + "(service references etc.)";
585  const AnsiString TTLabelStr11 = "Timetable" + NL + "start time";
586 
587  const AnsiString TTLabelStr12 = "NB: WITHIN SERVICES commas must" + NL + "not be used (have special meanings)," + NL +
588  "and semicolons may only be used to" + NL + "separate service components.";
589 
590  const AnsiString TTLabelStr13 = "+ linked shuttle service ref. ';' finishing service ref." + NL + "+ linked shuttle service ref.";
591 
592  const AnsiString TTLabelStr15 = "Repeat the service + ';' minutes between repeats ';' digit increment ';' number of repeats (last line of service)";
593 
594  TTLabel1->Caption = TTLabelStr1;
595  TTLabel2->Caption = TTLabelStr2;
596  TTLabel3->Caption = TTLabelStr3;
597  TTLabel4->Caption = TTLabelStr4;
598  TTLabel5->Caption = TTLabelStr5;
599  TTLabel6->Caption = TTLabelStr6;
600  TTLabel7->Caption = TTLabelStr7;
601  TTLabel9->Caption = TTLabelStr9;
602  TTLabel11->Caption = TTLabelStr11; //changed Left property to 300 from 487 at v2.17.0 so less likely for a long name to overlap TTLabel8
603  TTLabel12->Caption = TTLabelStr12;
604  TTLabel13->Caption = TTLabelStr13;
605  TTLabel15->Caption = TTLabelStr15;
606 
607  SelectBitmap->TransparentColor = Utilities->clTransparent;
608  RailGraphics->ChangeAllTransparentColours(Utilities->clTransparent, clB5G5R5); // original colour is as loaded at this stage - white
610 
611  TextBox->Color = clB3G3R3;
612  MainScreen->Canvas->Brush->Color = Utilities->clTransparent;
613  MainScreen->Canvas->FillRect(MainScreen->ClientRect);
614 
615  if((Screen->Width < 1024) || (Screen->Height < 768))
616  {
617  ShowMessage("Please note that this program works best with a screen resolution of at least 1024 x 768. Please change if possible");
618  }
619  SkipFormResizeEvent = true; // added at v2.1.0
620  MasterClock->Enabled = true;
621  Visible = true; // make Interface form visible (set to false at design time) autocalls FormResize so it is skipped by SkipFormResizeEvent being true
622  WindowState = wsMaximized; // need this for full screen at start autocalls FormResize so it is skipped by SkipFormResizeEvent being true
623  MTBFEditBox->Left = MainScreen->Left + MainScreen->Width - MTBFEditBox->Width + 30; // new v2.4.0 30 is to place it above the positional panel
624  // has to come after Visible = true or doesn't show
625  MTBFLabel->Left = MainScreen->Left + MainScreen->Width - MTBFEditBox->Width + 30 - 55; // new v2.4.0 placed above and to the left of MTBFEditBox
626  PositionalPanel->Left = MainScreen->Left + MainScreen->Width; // changed at v2.4.0
627  PositionalPanel->Top = MainScreen->Top; // changed at v2.4.0
628  PositionalPanel->Height = MainScreen->Height; // changed at v2.4.0
629  AllSetUpFlag = true;
630  TotalTicks = 0;
632  SetLevel1Mode(131); // to reset background colour mode menu choices
633  Screen->Cursor = TCursor(-2); // Arrow
634  SkipFormResizeEvent = false; // added at v2.1.0
635  SelectedGraphicFileName = ""; // only set to null here so always has a value after use LoadUserGraphic
636 
637  FloatingPanel->Color = TColor(0xF0FFFF); // new v2.2.0, corrects floating panel background colour in Windows 10
638  DevelopmentPanel->Color = TColor(0xCCCCCC); // new v2.2.0 as above
639  TTStartTimeBox->Color = TColor(0x99FFFF); // cream
640  HighlightPanel->Color = TColor(0x33CCFF);
641  MTBFEditBox->Visible = false; // new at v2.4.0
642  MTBFLabel->Visible = false;
643  CancelSelectionFlag = false;
644  TTStartTimeIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, best to initialise to an invalid location to force errors if not set properly
647  Utilities->OverrideAndHideSignalBridgeMessage = false; // added at v2.5.1 to allow facing signals before bridges - with a warning
648  ConflictPanel->Visible = false;
649  TTClockAdjustWarningPanel->Visible = false;
650  TTClockAdjustWarningHide = false;
651  TwoLocationNamePanel->Visible = false;
652  TwoLocationNamePanelHide = false;
653  LastNonCtrlOrShiftKeyDown = -1; // set to no key
654  ClipboardChecked = false;
655  MMoveTrackSelFlag = false;
656  MMovePrefDirSelFlag = false;
660  NumPlayers = 0; //Multiplayer integer
661  MultiplayerMenu->Enabled = false;
662  MultiplayerMenu->Visible = false; //<-- only until multiplayer added (also temporarily false in designer)
663  CouplingFileLoadedFlag = false;
665  PlayerReadyToBeginFlag = false;
666  PlayerCancelJoinFlag = false;
668  HostInSessionFlag = false;
669  PlayerInSessionFlag = false;
670  LastHostDataReceived = TDateTime(0);//initial value
672  SkipTTActionsListBox->Visible = false;
673  SkipListHeaderPanel->Visible = false;
674  ReminderHeaderPanel->Visible = false;
676  ElapsedTimerRunning = false;
679  Utilities->MTBTSRs = Utilities->NilMTBTSRs; //set high initially
682  Utilities->CumulativeDelayedRandMinsAllTrains = 0; //added at v2.13.0
684  AllEntriesTTListBox->TopIndex = 0; //added at v2.13.0 to initialise the value (seemed to initialise to 0 without this but not documented)
685  FlashControlButton->Visible = false; //added at v2.15.0
686 
687  SigImagePanel->Left = (Interface->Width - SigImagePanel->Width) / 2; // added for v2.3.0
688 
689  // below added at v2.4.0 so able to load session files with the correct decimal point
690  Utilities->DecimalPoint = '.'; // default case is full stop
691  char *LocalNumericInformation = setlocale(LC_NUMERIC, ""); // need this to set lconv to the environment's numeric format
693  FirstPerfLogFormDisplay = true; //added at v2.13.0 for Performance log form
694  if(AnsiString(LocalNumericInformation).Length() == 0) // call failed, don't change decimal point in Utilities.cpp
695  {
696  Utilities->SetLocaleResultOK = false;
697  }
698  struct lconv Locale; // store this structure in memory (accessed via locale.h in Utilities.h)
699  struct lconv *conv = &Locale;
700  // read the locality conversion structure
701  conv = localeconv(); // this is what updates the structure
702  ExitHeatmaps(); //to set up initial parameters
703  Utilities->DecimalPoint = conv->decimal_point[0];
704  }
705 
706  catch(const EFOpenError &e)
707  {
708  TMsgDlgButtons But;
709  But << mbOK;
710  MessageDlg(e.Message + " - program must terminate", mtError, But, 0);
711  Application->Terminate();
712  }
713 
714  catch(const Exception &e)
715  {
716  TMsgDlgButtons But;
717  But << mbOK;
718  AnsiString Message = "A fatal error occurred during the program setup process, the program must terminate. Message = " + e.Message;
719  MessageDlg(Message, mtError, But, 0); // this message given first in case can't create the error log
720  ErrorLog(115, e.Message);
721  Application->Terminate();
722  }
723 }
724 
725 // ---------------------------------------------------------------------------
726 
728 {
729  // destructor
730  try
731  {
732  // rewrite ConfigFile with signal handedness, background colour, InitialDir values (may be same but no matter) & default track element length & speed limit
733  SaveConfigFile(1); //added at v2.11.0
734  DeleteFile(TempTTFileName); // added at v2.5.0 to prevent temporary files building up
735  SkipFormResizeEvent = true; // added at v2.1.0
736  delete NonSigRouteStartMarker;
737  delete SigRouteStartMarker;
738  delete AutoRouteStartMarker;
739  delete PointFlash;
740  delete SelectBitmap;
741  delete TrainController;
742  delete EveryPrefDir;
743  delete SelectPrefDir;
744  delete ConstructRoute;
745  delete ConstructPrefDir;
746  delete AllRoutes;
747  delete Track;
748  delete TextHandler;
749  delete HiddenDisplay;
750  delete HiddenScreen;
751  delete Display;
752  delete StaticFeaturesDisplay;
753  delete StaticFeaturesScreen;
754  delete RailGraphics;
755  delete Utilities;
756  delete session_api_; //added at v2.10.0
757  }
758  catch(const Exception &e)
759  {
760  ErrorLog(116, e.Message);
761  }
762 }
764 
765 // ---------------------------------------------------------------------------
766 
767 void __fastcall TInterface::FormCreate(TObject *Sender)
768 {
769  // these functions have to be defined here to take effect when application activated & deactivated
770  try
771  {
772  Application->OnDeactivate = AppDeactivate;
773  Application->OnActivate = AppActivate;
774  }
775  catch(const Exception &e)
776  {
777  ErrorLog(117, e.Message);
778  }
779 }
780 
781 // ---------------------------------------------------------------------------
782 
783 void __fastcall TInterface::AppDeactivate(TObject *Sender)
784 {
785  // pause operation if operating & stop the master clock
786  try
787  {
789  {
790  if(Track->RouteFlashFlag) // in case route building - cancels the route, freezes otherwise - reported
791  {
792  // by Matt Blades 30/06/11
796  Screen->Cursor = TCursor(-2); // Arrow
797  Track->RouteFlashFlag = false;
798  ClearandRebuildRailway(48); // to get rid of displayed route
799  }
800  if(Track->PointFlashFlag)
801  // added at v1.3.1 to prevent lockup for flashing points when deactivates. Notified by Ian Walker in his email of 25/03/13.
802  {
804  Track->PointFlashFlag = false;
806  Screen->Cursor = TCursor(-2); // Arrow
807  }
808  if(!HostInSessionFlag && !PlayerInSessionFlag) //don't pause if multiplay in session
809  {
812  }
813  }
815  {
816  MasterClock->Enabled = false; //keep enabled for comms in multiplayer
817  }
818  ClipboardChecked = false; // added at v2.8.0 to force a check of the clipboard (via ClockTimer2 & SetTrackModeMenu)
819  }
820  catch(const Exception &e)
821  {
822  ErrorLog(118, e.Message);
823  }
824 }
825 
826 // ---------------------------------------------------------------------------
827 
828 void __fastcall TInterface::AppActivate(TObject *Sender)
829 {
830  // restart the master clock providing Interface constructor has run
831  try
832  {
833  if(AllSetUpFlag)
834  {
835  MasterClock->Enabled = true; //will already be enabled if no multiplay
836  ClipboardChecked = false; // at v2.9.0 added here too as for some unknown reason it doesn't always work when just in deactivate
837  }
838  }
839  catch(const Exception &e)
840  {
841  ErrorLog(119, e.Message);
842  }
843 }
844 
845 // ---------------------------------------------------------------------------
846 
847 UnicodeString TInterface::GetVersion()
848 {
849  DWORD VersionHandle;
850  DWORD VersionSize;
851  LPBYTE pBuffer;
852  UnicodeString strVersion = L"N/A";
853 
854  VersionSize = GetFileVersionInfoSizeW(Application->ExeName.c_str(), &VersionHandle);
855  if(VersionSize)
856  {
857  pBuffer = new BYTE[VersionSize];
858 
859  if(GetFileVersionInfoW(Application->ExeName.c_str(), VersionHandle, VersionSize, pBuffer))
860  {
861  VS_FIXEDFILEINFO *fi;
862  UINT buflen;
863 
864  // uncomment strVersion and HIWORD alternates below when future CI implemented: sas@2.1.0
865  if(VerQueryValueW(pBuffer, L"\\", (void**)&fi, &buflen))
866  {
867  // strVersion.sprintf(L"%d.%d.%d (Build %d)",
868  strVersion.sprintf(L"%d.%d.%d", HIWORD(fi->dwFileVersionMS), LOWORD(fi->dwFileVersionMS), HIWORD(fi->dwFileVersionLS));
869  // HIWORD(fi->dwFileVersionLS), LOWORD(fi->dwFileVersionLS)
870  }
871  }
872  delete[]pBuffer;
873  }
874  return(L" v" + strVersion);
875 }
876 
877 // ---------------------------------------------------------------------------
878 // Track Build Interface
879 // ---------------------------------------------------------------------------
880 void __fastcall TInterface::BuildTrackMenuItemClick(TObject *Sender) // Mode Menu Item
881 {
882  try
883  {
884  TrainController->LogEvent("BuildTrackMenuItemClick");
885  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildTrackMenuItemClick");
887  SetLevel1Mode(0);
888  Utilities->CallLogPop(1159);
889  }
890  catch(const Exception &e)
891  {
892  ErrorLog(120, e.Message);
893  }
894 }
895 // ---------------------------------------------------------------------------
896 
897 void __fastcall TInterface::AddTrackButtonClick(TObject *Sender)
898 {
899  try
900  {
901  TrainController->LogEvent("AddTrackButtonClick");
902  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddTrackButtonClick");
903  ExitHeatmaps();
905  SetLevel1Mode(38);
908  Utilities->CallLogPop(1162);
909  }
910  catch(const Exception &e)
911  {
912  ErrorLog(121, e.Message);
913  }
914 }
915 
916 // ---------------------------------------------------------------------------
917 void __fastcall TInterface::SpeedButtonClick(TObject *Sender)
918 {
919  try
920  {
921  TrainController->LogEvent("SpeedButtonClick");
922  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SpeedButtonClick");
923  ReselectMenuItem->Enabled = false;
924  if(((TSpeedButton*)Sender)->Down)
925  {
926  CurrentSpeedButton = (TSpeedButton*)Sender;
927 // TrainController->LogEvent("SpeedButtonClick, " + CurrentSpeedButton->Tag); //v 1.3.1 - using non-AnsiString CurrentSpeedButton->Tag sends (for an unknown reason) a completely wrong string to LogEvent, usually "...(behind this message)"
928  TrainController->LogEvent("SpeedButtonClick, " + AnsiString(CurrentSpeedButton->Tag)); // new version //use for v1.3.2
929  if((Level2TrackMode == TrackSelecting) && (CurrentSpeedButton->Tag != 144))
930  // new addition at v2.6.0 to fill selected area with the element corresponding to CurrentSpeedButton
931  {
932  // 144 = level crossing & these not permitted
933  if((SelectRect.left != SelectRect.right) && (SelectRect.top != SelectRect.bottom) && SelectionValid)
934  {
935  Screen->Cursor = TCursor(-11); // Hourglass;
936  InfoPanel->Caption = "SELECTING: Filling area with chosen element";
937  bool FillSelectionFlag = false;
939  {
940  UnicodeString MessageStr =
941  "Existing elements won't be overwritten but if the chosen element can be named then any existing named "
942  "elements within or adjacent to the area will have their names removed.\n\n"
943  "Click 'Yes' to fill the area with the chosen element or 'No' to abort.\n\nThis message will not be shown again.";
944  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_YESNO);
945  if(button == IDYES)
946  {
947  FillSelectionFlag = true;
949  }
950  }
951  if(FillSelectionFlag || FillSelectionMessageSentFlag)
952  {
953  bool TrackLinkingRequiredFlag = true;
954  bool FoundFlag = false;
955  std::list<AnsiString> NamedElementList;
956  if((CurrentSpeedButton->Tag == 76) || (CurrentSpeedButton->Tag == 77) || (CurrentSpeedButton->Tag == 78) || (CurrentSpeedButton->Tag == 79) ||
957  (CurrentSpeedButton->Tag == 96) || (CurrentSpeedButton->Tag == 129) || (CurrentSpeedButton->Tag == 130) || (CurrentSpeedButton->Tag == 131) ||
958  (CurrentSpeedButton->Tag == 145) || (CurrentSpeedButton->Tag == 146))
959  {
960  for(int HLoc = SelectRect.Left - 1; HLoc < SelectRect.Right + 1; HLoc++)
961  {
962  for(int VLoc = SelectRect.Top - 1; VLoc < SelectRect.Bottom + 1; VLoc++)
963  {
964  int TVPos = Track->GetVectorPositionFromTrackMap(69, HLoc, VLoc, FoundFlag);
965  if(FoundFlag)
966  {
967  TTrackElement TE1 = Track->TrackElementAt(1632, TVPos);
968  if(TE1.ActiveTrackElementName != "")
969  {
970  NamedElementList.push_back(TE1.ActiveTrackElementName);
971  }
972  if(TE1.LocationName != "")
973  {
974  NamedElementList.push_back(TE1.LocationName);
975  }
976  }
977  FoundFlag = false;
978  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(35, HLoc, VLoc, FoundFlag);
979  if(FoundFlag)
980  {
981  TTrackElement TE2 = Track->InactiveTrackElementAt(1403, IMPair.first);
982  TTrackElement TE3 = Track->InactiveTrackElementAt(1404, IMPair.second);
983  if(TE2.ActiveTrackElementName != "") //don't think inactive elaments have active name but leave in just in case
984  {
985  NamedElementList.push_back(TE2.ActiveTrackElementName);
986  }
987  if(TE2.LocationName != "")
988  {
989  NamedElementList.push_back(TE2.LocationName);
990  }
991  if(TE3.ActiveTrackElementName != "")
992  {
993  NamedElementList.push_back(TE3.ActiveTrackElementName);
994  }
995  if(TE3.LocationName != "")
996  {
997  NamedElementList.push_back(TE3.LocationName);
998  }
999  }
1000  }
1001  }
1002  //now make list unique & blank all names contained in it
1003  NamedElementList.sort();
1004  NamedElementList.unique();
1005  AnsiString CurrentName = "";
1006  int HPos, VPos;
1007  while(!NamedElementList.empty())
1008  {
1009  CurrentName = NamedElementList.front();
1010  NamedElementList.pop_front();
1011  for(unsigned int TVNamePos = 0; TVNamePos < Track->TrackVector.size(); TVNamePos++)
1012  {
1013  if(Track->TrackElementAt(1633, TVNamePos).ActiveTrackElementName == CurrentName)
1014  {
1015  Track->TrackElementAt(1634, TVNamePos).ActiveTrackElementName = "";
1016  }
1017  if(Track->TrackElementAt(1635, TVNamePos).LocationName == CurrentName)
1018  {
1019  Track->TrackElementAt(1636, TVNamePos).LocationName = "";
1020  }
1021  }
1022  for(unsigned int ITVNamePos = 0; ITVNamePos < Track->InactiveTrackVector.size(); ITVNamePos++)
1023  {
1024  if(Track->InactiveTrackElementAt(1405, ITVNamePos).ActiveTrackElementName == CurrentName)
1025  {
1026  Track->InactiveTrackElementAt(1406, ITVNamePos).ActiveTrackElementName = "";
1027  }
1028  if(Track->InactiveTrackElementAt(1407, ITVNamePos).LocationName == CurrentName)
1029  {
1030  Track->InactiveTrackElementAt(1408, ITVNamePos).LocationName = "";
1031  }
1032  }
1033  if(TextHandler->FindText(6, CurrentName, HPos, VPos)) //returns position in HPos & Vpos if found
1034  {
1035  TextHandler->TextErase(8, HPos, VPos, CurrentName);
1036  }
1037  }
1038  }
1039  for(int HLoc = SelectRect.left; HLoc < SelectRect.right; HLoc++)
1040  {
1041  for(int VLoc = SelectRect.top; VLoc < SelectRect.bottom; VLoc++)
1042  {
1043  Track->PlotAndAddTrackElement(3, CurrentSpeedButton->Tag, 0, HLoc, VLoc, TrackLinkingRequiredFlag, false, false);
1044  //false for internal checks & false for perform name search
1045  //above now has extra zero 'Aspect' (3rd parameter) at v2.2.0 so can distinguish between adding track and pasting
1046  //for signals - 0 combined with TrackType == SignalPost means adding and not pasting
1047  }
1048  }
1050  }
1051  Track->SetTrackFinished(false);
1052  ClearandRebuildRailway(80); // to remove selection outline
1053  SelectionValid = false;
1054  Track->CopyFlag = false;
1056  ResetSelectRect();
1057  SetLevel1Mode(139);
1059  SetLevel2TrackMode(66);
1060  Screen->Cursor = TCursor(-2); // Arrow
1061  ReselectMenuItem->Enabled = true; // allow when filling areas
1062  }
1063  }
1064  }
1065  else
1066  {
1067  CurrentSpeedButton = 0;
1068  }
1069  Utilities->CallLogPop(1163);
1070  }
1071  catch(const Exception &e)
1072  {
1073  ErrorLog(122, e.Message);
1074  }
1075 }
1076 
1077 // ---------------------------------------------------------------------------
1078 void __fastcall TInterface::TrackOKButtonClick(TObject *Sender)
1079 {
1080  try
1081  {
1082  TrainController->LogEvent("TrackOKButtonClick");
1083  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TrackOKButtonClick");
1084  Screen->Cursor = TCursor(-11); // Hourglass; //added at v2.11.1
1085  SelectionValid = false;
1087  bool LocError;
1088  int HLoc, VLoc;
1089  // erase any corrupted PrefDirs then rebuild track & PrefDir vectors
1090 // EveryPrefDir->EraseCorruptedElementsAfterTrackBuild(); not needed after dispensed with blank track elements for erased elements
1091  ExitHeatmaps();
1092  if(!(Track->TryToConnectTrack(0, LocError, HLoc, VLoc, true))) // true for give messages
1093  // if successful repositions TrackVector & builds TrackMap
1094  {
1095  if(LocError) // links not complete or other error - show offending element
1096  {
1097  while((Display->DisplayOffsetH - HLoc) > 0)
1098  {
1099  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
1100  }
1101  while((HLoc - Display->DisplayOffsetH) > (Utilities->ScreenElementWidth - 1))
1102  {
1104  }
1105  while((Display->DisplayOffsetV - VLoc) > 0)
1106  {
1107  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
1108  }
1109  while((VLoc - Display->DisplayOffsetV) > (Utilities->ScreenElementHeight - 1))
1110  {
1112  }
1114  Display->InvertElement(0, HLoc * 16, VLoc * 16);
1115  ShowMessage("Incomplete track or other error - see highlighted element (it may be behind this message "
1116  "which can be moved by left clicking the mouse in the title bar and dragging it).");
1117  ClearandRebuildRailway(1); // to clear inversion
1119  SetLevel1Mode(39);
1120  Level2TrackMode = AddTrack; // go to add track regardless of where started from
1121  SetLevel2TrackMode(3);
1122  Screen->Cursor = TCursor(-2); // Arrow //added at v2.11.1
1123  Utilities->CallLogPop(0);
1124  return;
1125  }
1126  else
1127  {
1128  // reach here if there are no track elements
1129  ShowMessage("Unable to set any track links");
1131  SetLevel1Mode(40);
1133  SetLevel2TrackMode(4); // go to add track regardless of where started from
1134  Screen->Cursor = TCursor(-2); // Arrow //added at v2.11.1
1135  Utilities->CallLogPop(1);
1136  return;
1137  }
1138  }
1139  else
1140  {
1141  // success so far as track is concerned ('TrackFinished' set in TryToConnectTrack)
1142  EveryPrefDir->RebuildPrefDirVector(0); // from TrackMap
1143  }
1144 // success if reach here ('TrackFinished' set in TryToConnectTrack)
1145  if(Level2TrackMode == AddTrack)
1146  {
1149  SetLevel1Mode(41);
1150  SetLevel2TrackMode(5);
1151  }
1152  else
1153  {
1155  SetLevel1Mode(36); // back to TrackMode if not in AddTrack mode
1156  }
1157  ShowMessage("Successful Completion");
1158  Screen->Cursor = TCursor(-2); // Arrow //added at v2.11.1
1159  Utilities->CallLogPop(2);
1160  }
1161  catch(const Exception &e)
1162  {
1163  ErrorLog(3, e.Message);
1164  }
1165 }
1166 
1167 // ---------------------------------------------------------------------------
1168 void __fastcall TInterface::SetGapsButtonClick(TObject *Sender)
1169 {
1170  try
1171  {
1172  TrainController->LogEvent("SetGapsButtonClick");
1173  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SetGapsButtonClick");
1174  SelectionValid = false;
1175  ReselectMenuItem->Enabled = false;
1176  ExitHeatmaps();
1178  SetLevel1Mode(42);
1180  SetLevel2TrackMode(6);
1181  Utilities->CallLogPop(1164);
1182  }
1183  catch(const Exception &e)
1184  {
1185  ErrorLog(123, e.Message);
1186  }
1187 }
1188 
1189 // ---------------------------------------------------------------------------
1190 void __fastcall TInterface::AddTextButtonClick(TObject *Sender)
1191 {
1192  try
1193  {
1194  TrainController->LogEvent("AddTextButtonClick");
1195  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddTextButtonClick");
1196  ExitHeatmaps();
1198  SetLevel1Mode(43);
1200  SetLevel2TrackMode(7);
1201  Utilities->CallLogPop(1165);
1202  }
1203  catch(const Exception &e)
1204  {
1205  ErrorLog(124, e.Message);
1206  }
1207 }
1208 
1209 // ---------------------------------------------------------------------------
1210 void __fastcall TInterface::MoveTextOrGraphicButtonClick(TObject *Sender)
1211 {
1212  try
1213  {
1214  TrainController->LogEvent("MoveTextOrGraphicButtonClick");
1215  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MoveTextOrGraphicButtonClick");
1216  ExitHeatmaps();
1218  SetLevel1Mode(44);
1220  SetLevel2TrackMode(8);
1221  Utilities->CallLogPop(1166);
1222  }
1223  catch(const Exception &e)
1224  {
1225  ErrorLog(125, e.Message);
1226  }
1227 }
1228 
1229 // ---------------------------------------------------------------------------
1230 void __fastcall TInterface::TextBoxKeyPress(TObject *Sender, char &Key)
1231 {
1232  try
1233  {
1234  TrainController->LogEvent("TextBoxKeyPress," + AnsiString(Key));
1235  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TextBoxKeyPress," + AnsiString(Key));
1236  if(Key == '\x0D') // CR
1237  {
1238  if(TextBox->Text != "") // if blank then don't save
1239  {
1240  if(Display->GetFont()->Color == clB5G5R5) // white
1241  {
1242  TFont *TempFont = new TFont;
1243  TempFont->Assign(Display->GetFont());
1244  TempFont->Color = clB0G0R0; // change to black for vector & saving
1245  Display->SetFont(TempFont);
1246  delete TempFont;
1247  }
1248  TFont *DisplayFont = Display->GetFont();
1249  TTextItem TempText = TTextItem(Text_X, Text_Y, TextBox->Text, DisplayFont);
1250  TempText.Font = DisplayFont; // may have been changed in above constructor when returned as reference
1252  ResetChangedFileDataAndCaption(12, true); // moved here from MainScreenMouseDown2 after 2.7.0 in case nothing changed
1253  }
1254  EditMenu->Enabled = true;
1255  TextBox->Visible = false;
1256  SetLevel2TrackMode(56); // to enable 'move text' if first text item added
1257  }
1258  else if(Key == '\x1B') // escape
1259  {
1260  if(TextBox->Text != "")
1261  {
1262  ResetChangedFileDataAndCaption(28, true); // added here after 2.7.0 in case replaced existing text (which is selected) with blank
1263  }
1264  TextBox->Visible = false;
1265  }
1266  Utilities->CallLogPop(3);
1267  }
1268  catch(const Exception &e)
1269  {
1270  ErrorLog(4, e.Message);
1271  }
1272 }
1273 
1274 // ---------------------------------------------------------------------------
1275 void __fastcall TInterface::LocationNameButtonClick(TObject *Sender)
1276 {
1277  try
1278  {
1279  TrainController->LogEvent("LocationNameButtonClick");
1280  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LocationNameButtonClick");
1281  ExitHeatmaps();
1283  SetLevel1Mode(45);
1285  SetLevel2TrackMode(9);
1286  Utilities->CallLogPop(1167);
1287  }
1288  catch(const Exception &e)
1289  {
1290  ErrorLog(126, e.Message);
1291  }
1292 }
1293 
1294 // ---------------------------------------------------------------------------
1295 void __fastcall TInterface::LocationNameKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
1296 {
1297  try
1298  {
1299  TrainController->LogEvent("LocationNameKeyUp," + AnsiString(Key));
1300  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LocationNameKeyUp," + AnsiString(Key));
1301  if(Track->LNPendingList.empty())
1302  {
1303  ShowMessage("Error, location name being entered without an entry in LNPendingList");
1305  SetLevel1Mode(46);
1307  SetLevel2TrackMode(10);
1308  Utilities->CallLogPop(4);
1309  return;
1310  }
1311  if(Key == '\x1B') // escape
1312  {
1313  Track->LNPendingList.clear(); // get rid of existing entry
1315  SetLevel1Mode(47);
1317  SetLevel2TrackMode(11);
1318  Utilities->CallLogPop(5);
1319  return;
1320  }
1321  if(Key == '\x0D') //enter the name
1322  {
1323  Screen->Cursor = TCursor(-11); // Hourglass;
1325  ResetChangedFileDataAndCaption(8, true); // moved here after 2.7.0 from mainScreenMouseDown2 in case nothing changed
1326  AnsiString ExistingName;
1327  LocationNameTextBox->Text = LocationNameTextBox->Text.Trim();
1328 // added at v2.6.1 to prevent added spaces because they skip the different location same name check
1329  if(Track->LNPendingList.front() > -1)
1330  {
1331  ExistingName = Track->InactiveTrackElementAt(27, Track->LNPendingList.front()).LocationName;
1332  }
1333  else
1334  {
1335  ExistingName = Track->TrackElementAt(425, -1 - (Track->LNPendingList.front())).LocationName;
1336  }
1337  if(Track->LocationNameAllocated(1, LocationNameTextBox->Text) && (ExistingName != LocationNameTextBox->Text))
1338  {
1339  // name allocated to a different location
1340 
1341  if(LocationNameTextBox->Text != "") //if new name is NULL then don't give message but carry out all other
1342  { //functions - added at v2.14.0
1343  UnicodeString MessageStr = UnicodeString("Another location named '") + LocationNameTextBox->Text +
1344  UnicodeString("' already exists. If you continue its name will be erased. Do you wish to continue?");
1345  int button = Application->MessageBox(MessageStr.c_str(), L"Warning!", MB_YESNO | MB_ICONWARNING);
1346  if(button == IDNO)
1347  {
1348  Track->LNPendingList.clear(); // get rid of existing entry
1349  Screen->Cursor = TCursor(-2); // Arrow
1351  SetLevel1Mode(48);
1353  SetLevel2TrackMode(12);
1354  Utilities->CallLogPop(6);
1355  return;
1356  }
1357  }
1359  Track->EnterLocationName(1, LocationNameTextBox->Text, false);
1360  int HPos, VPos;
1361  bool UseExistingPosition = false;
1362  if(EraseLocationNameText(0, LocationNameTextBox->Text, HPos, VPos))
1363  {
1364  ;
1365  } // condition not used
1366 
1367  // above may be redundant at v1.1.0 due to name erase in EnterLocationName
1368  // but, the location to be named may also have an existing name, in which case that needs to be erased
1369  // and the position re-used
1370  if(ExistingName != "")
1371  {
1372  if(EraseLocationNameText(3, ExistingName, HPos, VPos))
1373  {
1374  UseExistingPosition = true; // may be redundant at v1.1.0 due to name erase in EnterLocationName
1375  }
1376  }
1377  AddLocationNameText(0, LocationNameTextBox->Text, HPos, VPos, UseExistingPosition);
1378  Screen->Cursor = TCursor(-2); // Arrow
1380  SetLevel1Mode(49);
1382  SetLevel2TrackMode(13);
1383  Utilities->CallLogPop(7);
1384  return;
1385  }
1386  else if(Track->LocationNameAllocated(2, LocationNameTextBox->Text) && (ExistingName == LocationNameTextBox->Text))
1387  {
1388  // same name being entered again
1389  Track->LNPendingList.clear(); // get rid of existing entry as the location already has this name
1390  // but in case the name is not already in text vector erase it and re-add it
1391  // if it wasn't in the vector erasing it has no effect
1392  int HPos, VPos;
1393  bool UseExistingPosition = false;
1394  if(EraseLocationNameText(2, LocationNameTextBox->Text, HPos, VPos))
1395  {
1396  UseExistingPosition = true;
1397  }
1398  // above may be redundant at v1.1.0 due to name erase in EnterLocationName
1399  AddLocationNameText(2, LocationNameTextBox->Text, HPos, VPos, UseExistingPosition);
1400  Screen->Cursor = TCursor(-2); // Arrow
1402  SetLevel1Mode(50);
1404  SetLevel2TrackMode(14);
1405  Utilities->CallLogPop(8);
1406  return;
1407  }
1408  else
1409  {
1410  // either a new name for an unnamed location, or a different name for a named location
1411  // check validity of entry
1412  AnsiString LocStr = LocationNameTextBox->Text;
1413  LocStr = LocStr.Trim(); // strip leading & trailing spaces, and control characters
1414  LocationNameTextBox->Text = LocStr; // reset this as used below
1415 /* drop this, now covered by ...Trim() above
1416  //strip leading spaces
1417  while((LocStr != "") && (LocStr[1] == ' '))
1418  {
1419  LocStr = LocStr.SubString(2, LocStr.Length()-1);
1420  }
1421 */
1422 // if((LocStr != "") && (LocStr[1] >= '0') && (LocStr[1] <= '9')) // changed at v2.16.0 to allow locs to begin with digits but not 'digit-digit-colon'
1423  if((LocStr.Length() >= 3) && (LocStr[1] >= '0') && (LocStr[1] <= '9') && (LocStr[2] >= '0') && (LocStr[2] <= '9') && (LocStr[3] == ':'))
1424  {
1425  Screen->Cursor = TCursor(-2); // Arrow
1426  ShowMessage("Location name can't begin with 'digit-digit-colon' as that is treated as the start of a time entry");
1428  SetLevel1Mode(51);
1430  SetLevel2TrackMode(15);
1431  Utilities->CallLogPop(776);
1432  return;
1433  }
1434 
1435  if(LocStr.Length() > 50)
1436  {
1437  Screen->Cursor = TCursor(-2); // Arrow
1438  ShowMessage("Location name too long, 50 characters maximum");
1440  SetLevel1Mode(122);
1442  SetLevel2TrackMode(55);
1443  Utilities->CallLogPop(1735);
1444  return;
1445  }
1446  for(int x = 1; x <= LocStr.Length(); x++)
1447  {
1448  char Ch = LocStr[x];
1449 // if((Ch != ' ') && (Ch != '&') && (Ch != '(') && (Ch != ')') && (Ch != ':') && (Ch != 39) && (Ch != '.') && (Ch != '-') && (Ch != '+') &&
1450 // (Ch != '/') && ((Ch < '0') || (Ch > '9')) && ((Ch < 'A') || (Ch > 'Z')) && ((Ch < 'a') || (Ch > 'z')))
1451 // Above removed at v2.16.0 to allow extended characters in location names
1452  if(((Ch < 32) && (Ch >= 0)) || (Ch == ',') || (Ch == ';'))
1453  {
1454  Screen->Cursor = TCursor(-2); // Arrow
1455  ShowMessage(
1456 // "Location name contains one or more invalid characters, must be alphanumeric, brackets, space, full stop, colon, inverted comma, '-', '+', '/' or '&&'"); //changed at v2.16.0 to allow extended characters in location names
1457  "Location name contains one or more invalid characters - must not contain control characters, ';' or ','");
1459  SetLevel1Mode(52);
1461  SetLevel2TrackMode(16);
1462  Utilities->CallLogPop(777);
1463  return;
1464  }
1465  }
1466  if(LocStr == "cdt") // this has Time:Command which could be confused with Time:Loc
1467  {
1468  Screen->Cursor = TCursor(-2); // Arrow
1469  ShowMessage("Location name cannot be 'cdt', this name would interfere with the timetable");
1471  SetLevel1Mode(53);
1473  SetLevel2TrackMode(17);
1474  Utilities->CallLogPop(778);
1475  return;
1476  }
1477  Track->EnterLocationName(2, LocStr, false);
1478  // need to check if the location already has a name, and if so erase it from the textvector
1479  int HPos, VPos;
1480  bool UseExistingPosition = false;
1481  if(ExistingName != "")
1482  {
1483  if(EraseLocationNameText(1, ExistingName, HPos, VPos))
1484  {
1485  UseExistingPosition = true; // may be redundant at v1.1.0 due to name erase in EnterLocationName
1486  }
1487  }
1488  AddLocationNameText(1, LocationNameTextBox->Text, HPos, VPos, UseExistingPosition);
1489  Screen->Cursor = TCursor(-2); // Arrow
1491  SetLevel1Mode(54);
1493  SetLevel2TrackMode(18);
1494  Utilities->CallLogPop(9);
1495  return;
1496  }
1497  }
1498  Screen->Cursor = TCursor(-2); // Arrow
1499  Utilities->CallLogPop(10);
1500  }
1501  catch(const Exception &e)
1502  {
1503  ErrorLog(5, e.Message);
1504  }
1505 }
1506 
1507 // ---------------------------------------------------------------------------
1508 void __fastcall TInterface::SetLengthsButtonClick(TObject *Sender)
1509 {
1510  try
1511  {
1512  TrainController->LogEvent("SetLengthsButtonClick");
1513  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SetLengthsButtonClick");
1514  SelectLengthsFlag = false;
1515  ConstructPrefDir->ExternalClearPrefDirAnd4MultiMap(); // added for extended distances
1517  SetLevel1Mode(55);
1519  SetLevel2TrackMode(19);
1520  Utilities->CallLogPop(1168);
1521  }
1522  catch(const Exception &e)
1523  {
1524  ErrorLog(127, e.Message);
1525  }
1526 }
1527 
1528 // ---------------------------------------------------------------------------
1529 void __fastcall TInterface::LengthOKButtonClick(TObject *Sender)
1530 {
1531  try
1532  {
1533  TrainController->LogEvent("LengthOKButtonClick," + DistanceBox->Text + "," + SpeedLimitBox->Text);
1534  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LengthOKButtonClick");
1535  ResetChangedFileDataAndCaption(4, true); // moved here after 2.7.0 so only need to save if something changed
1536  int Dist = 0, SpeedLimit = 0;
1537  AnsiString DistanceStr = DistanceBox->Text;
1538  if(SelectLengthsFlag && (DistanceStr == ""))
1539  {
1540  DistanceStr = "No change";
1541  }
1542  AnsiString SpeedStr = SpeedLimitBox->Text;
1543  if(SelectLengthsFlag && (SpeedStr == ""))
1544  {
1545  SpeedStr = "No change";
1546  }
1547  if(SelectLengthsFlag)
1548  {
1549  if(DistanceStr == "No change")
1550  {
1551  Dist = -1; // i.e.don't change
1552  }
1553  if(SpeedStr == "No change")
1554  {
1555  SpeedLimit = -1; // i.e.don't change
1556  }
1557  }
1558  else
1559  {
1560  if(DistanceStr == AnsiString(OverallDistance))
1561  {
1562  Dist = -1; // i.e.don't change
1563  }
1564  if((SpeedStr == "Mixed") || (SpeedStr == AnsiString(OverallSpeedLimit)))
1565  {
1566  SpeedLimit = -1; // i.e.don't change
1567  }
1568  }
1569  if(((Dist != -1) && (DistanceStr.Length() > 6)) || ((SpeedLimit != -1) && (SpeedStr.Length() > 6)))
1570  {
1571  ShowMessage("One or more entries too long");
1572  Utilities->CallLogPop(11);
1573  return;
1574  }
1575  if((DistanceStr == "") || (SpeedStr == ""))
1576  {
1577  ShowMessage("One or more entries blank");
1578  Utilities->CallLogPop(12);
1579  return;
1580  }
1581  if(SelectLengthsFlag && (Dist != -1))
1582  {
1583  for(int x = 1; x <= DistanceStr.Length(); x++)
1584  {
1585  if((DistanceStr[x] < '0') || (DistanceStr[x] > '9'))
1586  {
1587  ShowMessage("Track length value must be a positive whole number, or blank for no change");
1588  Utilities->CallLogPop(1415);
1589  return;
1590  }
1591  }
1592  }
1593  if(!SelectLengthsFlag)
1594  {
1595  for(int x = 1; x <= DistanceStr.Length(); x++)
1596  {
1597  if((DistanceStr[x] < '0') || (DistanceStr[x] > '9'))
1598  {
1599  ShowMessage("Distance must be a positive whole number");
1600  Utilities->CallLogPop(13);
1601  return;
1602  }
1603  }
1604  }
1605  if(SelectLengthsFlag && (SpeedLimit != -1))
1606  {
1607  for(int x = 1; x <= SpeedStr.Length(); x++)
1608  {
1609  if((SpeedStr[x] < '0') || (SpeedStr[x] > '9'))
1610  {
1611  ShowMessage("Speed limit must be a positive whole number, or blank for no change");
1612  Utilities->CallLogPop(1416);
1613  return;
1614  }
1615  }
1616  }
1617  if(!SelectLengthsFlag && (SpeedStr != "Mixed"))
1618  {
1619  for(int x = 1; x <= SpeedStr.Length(); x++)
1620  {
1621  if((SpeedStr[x] < '0') || (SpeedStr[x] > '9'))
1622  {
1623  ShowMessage("Speed limit must be a positive whole number, or 'Mixed'");
1624  Utilities->CallLogPop(14);
1625  return;
1626  }
1627  }
1628  }
1629  if(Dist != -1)
1630  {
1631  Dist = DistanceStr.ToInt();
1632  }
1633  if(SpeedLimit != -1)
1634  {
1635  SpeedLimit = SpeedStr.ToInt();
1636  }
1637 /* don't need this with new condition below
1638  if(SelectLengthsFlag && (Dist != -1) && (Dist < 20))
1639  {
1640  ShowMessage("Track length value must be a minimum of 10m, setting to 10m");
1641  Dist = 20;
1642  }
1643 */
1644  if(((Dist != -1) && (Dist < 10)) || ((SpeedLimit != -1) && (SpeedLimit < 10)) || ((SpeedLimit != -1) && (SpeedLimit > TTrain::MaximumSpeedLimit)))
1645  // new limiting values for v0.6 (used only to fail at either value 0); added TTrain::MaxSpeedLimit at v2.1.0
1646  {
1647  ShowMessage("Lengths must be 10m or more, and speeds must be between 10km/h and 400km/h"); // changed at v2.1.0 to limit max speed
1648  Utilities->CallLogPop(15);
1649  return;
1650  }
1651  DistanceBox->Text = "";
1652  SpeedLimitBox->Text = "";
1653  if(SelectLengthsFlag)
1654  {
1655  int LowSelectHLoc = SelectBitmapHLoc;
1656  int HighSelectHLoc = SelectBitmapHLoc + (SelectBitmap->Width / 16);
1657  int LowSelectVLoc = SelectBitmapVLoc;
1658  int HighSelectVLoc = SelectBitmapVLoc + (SelectBitmap->Height / 16);
1659  bool FoundFlag;
1660  bool NamedLocPresent = false;
1661  if((Dist != -1) && (Dist != Utilities->DefaultTrackLength))
1662  {
1663  for(int x = LowSelectHLoc; x < HighSelectHLoc; x++)
1664  {
1665  for(int y = LowSelectVLoc; y < HighSelectVLoc; y++)
1666  {
1668  {
1669  NamedLocPresent = true;
1670  }
1671  }
1672  }
1673  }
1674  if(NamedLocPresent && (Dist < 50)) // changed in v2.4.0
1675  {
1676  if(!TooShortMessageSentFlag) //added at v2.9.1
1677  {
1678  ShowMessage("Note: Named location elements are quite short. If they are too short the simulation might be too unrealistic.\n\nThis message will not be shown again.");
1679  TooShortMessageSentFlag = true; //added at v2.9.1
1680  }
1681  }
1682  if(NamedLocPresent && (Dist > 200)) // changed in v2.4.0
1683  {
1684  if(!TooLongMessageSentFlag) //added at v2.9.1
1685  {
1686  ShowMessage("Note: Named location elements are quite long. If they are too long the simulation might be too unrealistic.\n\nThis message will not be shown again.");
1687  TooLongMessageSentFlag = true; //added at v2.9.1
1688  }
1689  }
1690  for(int x = LowSelectHLoc; x < HighSelectHLoc; x++)
1691  {
1692  for(int y = LowSelectVLoc; y < HighSelectVLoc; y++)
1693  {
1694  int VecPos = Track->GetVectorPositionFromTrackMap(34, x, y, FoundFlag);
1695  if(FoundFlag)
1696  {
1697  if(Dist > -1) // && !(Track->IsPlatformOrNamedNonStationLocationPresent(7, x, y)))
1698  {
1699  Track->TrackElementAt(692, VecPos).Length01 = Dist;
1700  if(Track->TrackElementAt(693, VecPos).Length23 != -1)
1701  {
1702  Track->TrackElementAt(694, VecPos).Length23 = Dist;
1703  }
1704  }
1705  if(SpeedLimit > -1)
1706  {
1707  Track->TrackElementAt(695, VecPos).SpeedLimit01 = SpeedLimit;
1708  if(Track->TrackElementAt(696, VecPos).SpeedLimit23 != -1)
1709  {
1710  Track->TrackElementAt(697, VecPos).SpeedLimit23 = SpeedLimit;
1711  }
1712  }
1713  }
1714  }
1715  }
1716  TrackLengthPanel->Visible = false;
1717  SelectLengthsFlag = false; // go back to normal distance setting mode
1718  }
1719  else
1720  {
1721  SetTrackLengths(1, Dist, SpeedLimit);
1722  }
1724  SetLevel1Mode(57);
1726  SetLevel2TrackMode(21);
1727  Utilities->CallLogPop(16);
1728  }
1729  catch(const Exception &e)
1730  {
1731  ErrorLog(6, e.Message);
1732  }
1733 }
1734 
1735 // ---------------------------------------------------------------------------
1736 void __fastcall TInterface::LengthCancelButtonClick(TObject * Sender)
1737 {
1738  try
1739  {
1740  TrainController->LogEvent("LengthCancelButtonClick");
1741  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LengthCancelButtonClick");
1742  DistanceBox->Text = "";
1743  SpeedLimitBox->Text = "";
1744  TrackLengthPanel->Visible = false;
1745  SelectLengthsFlag = false; // go back to normal distance setting mode
1747  SetLevel1Mode(59);
1749  SetLevel2TrackMode(23);
1750  Utilities->CallLogPop(1169);
1751  }
1752  catch(const Exception &e)
1753  {
1754  ErrorLog(128, e.Message);
1755  }
1756 }
1757 
1758 // ---------------------------------------------------------------------------
1759 void __fastcall TInterface::ResetDefaultLengthButtonClick(TObject *Sender)
1760 {
1761  try
1762  {
1763  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ResetDefaultLengthButtonClick");
1764  TMsgDlgButtons Buttons;
1765  Buttons << mbYes << mbNo;
1766  if(MessageDlg("This will reset the selected elements to default lengths & speed limits. Proceed?", mtWarning, Buttons, 0) == mrNo)
1767  {
1768  // leave all as was before
1769  Utilities->CallLogPop(17);
1770  return;
1771  }
1772  else
1773  {
1774  TrainController->LogEvent("Accepted ResetDefaultLengthButtonClick");
1775  ResetChangedFileDataAndCaption(25, true); // added after 2.7.0 so only need to save if something changed
1776  DistanceBox->Text = "";
1777  SpeedLimitBox->Text = "";
1778  if(SelectLengthsFlag)
1779  {
1780  int LowSelectHLoc = SelectBitmapHLoc;
1781  int HighSelectHLoc = SelectBitmapHLoc + (SelectBitmap->Width / 16);
1782  int LowSelectVLoc = SelectBitmapVLoc;
1783  int HighSelectVLoc = SelectBitmapVLoc + (SelectBitmap->Height / 16);
1784  bool FoundFlag;
1785  for(int x = LowSelectHLoc; x < HighSelectHLoc; x++)
1786  {
1787  for(int y = LowSelectVLoc; y < HighSelectVLoc; y++)
1788  {
1789  int VecPos = Track->GetVectorPositionFromTrackMap(35, x, y, FoundFlag);
1790  if(FoundFlag)
1791  {
1793  if(Track->TrackElementAt(699, VecPos).Length23 != -1)
1794  {
1796  }
1798  if(Track->TrackElementAt(702, VecPos).SpeedLimit23 != -1)
1799  {
1801  }
1802  }
1803  }
1804  }
1805  TrackLengthPanel->Visible = false;
1806 // ClearandRebuildRailway(47); don't need this
1807  SelectLengthsFlag = false; // go back to normal distance setting mode
1808  }
1809  else
1810  {
1811  TrackLengthPanel->Visible = false;
1812  bool FoundFlag;
1813  if(ConstructPrefDir->PrefDirSize() == 0)
1814  {
1815  Utilities->CallLogPop(1120);
1816  return;
1817  }
1818  for(unsigned int x = 0; x < ConstructPrefDir->PrefDirSize(); x++)
1819  {
1820  TPrefDirElement PrefDirElement = ConstructPrefDir->GetFixedPrefDirElementAt(169, x);
1821  TTrackElement & TrackElement = Track->TrackElementAt(37, Track->GetVectorPositionFromTrackMap(40, PrefDirElement.HLoc, PrefDirElement.VLoc,
1822  FoundFlag));
1823  if((TrackElement.TrackType == Points) || (TrackElement.TrackType == Crossover) || (TrackElement.TrackType == Bridge))
1824  // only set the relevant track to default length & speed limit
1825  {
1826  if((PrefDirElement.GetELinkPos() < 2) && (PrefDirElement.GetXLinkPos() < 2)) // could be one of each for points
1827  {
1828  TrackElement.Length01 = Utilities->DefaultTrackLength;
1829  TrackElement.SpeedLimit01 = Utilities->DefaultTrackSpeedLimit; // 200km/h = 125mph
1830  }
1831  else
1832  {
1833  TrackElement.Length23 = Utilities->DefaultTrackLength;
1834  TrackElement.SpeedLimit23 = Utilities->DefaultTrackSpeedLimit; // 200km/h = 125mph
1835  }
1836  }
1837  else // any other 1 track element, including platforms being present
1838  {
1839  if((PrefDirElement.GetELinkPos() > 1) && (PrefDirElement.GetXLinkPos() > 1))
1840  {
1841  throw Exception("Error, XLinkPos > 1 in SetOneDefaultTrackLength at " + AnsiString(TrackElement.HLoc) + " & " +
1842  AnsiString(TrackElement.VLoc));
1843  }
1844  TrackElement.Length01 = Utilities->DefaultTrackLength;
1845  TrackElement.SpeedLimit01 = Utilities->DefaultTrackSpeedLimit; // 200km/h = 125mph
1846  TrackElement.Length23 = -1;
1847  TrackElement.SpeedLimit23 = -1;
1848  }
1849  }
1850  }
1852  SetLevel1Mode(61);
1854  SetLevel2TrackMode(25);
1855  }
1856  Utilities->CallLogPop(18);
1857  }
1858  catch(const Exception &e)
1859  {
1860  ErrorLog(7, e.Message);
1861  }
1862 }
1863 
1864 // ---------------------------------------------------------------------------
1865 void __fastcall TInterface::RestoreAllDefaultLengthsButtonClick(TObject *Sender)
1866 {
1867  try
1868  {
1869  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RestoreAllDefaultLengthsButtonClick");
1870  TMsgDlgButtons Buttons;
1871  Buttons << mbYes << mbNo;
1872  if(MessageDlg("This will reset ALL track elements to default lengths & speed limits. Proceed?", mtWarning, Buttons, 0) == mrNo)
1873  {
1874  // leave all as was before
1875  Utilities->CallLogPop(19);
1876  return;
1877  }
1878  else
1879  {
1881  }
1882  TrainController->LogEvent("Accepted RestoreAllDefaultLengthsButtonClick");
1883  ResetChangedFileDataAndCaption(26, true); // added after 2.7.0 so only need to save if something changed
1884  DistanceBox->Text = "";
1885  SpeedLimitBox->Text = "";
1886  TrackLengthPanel->Visible = false;
1887  SelectLengthsFlag = false; // go back to normal distance setting mode
1889  SetLevel1Mode(63);
1891  SetLevel2TrackMode(27);
1892  Utilities->CallLogPop(20);
1893  }
1894  catch(const Exception &e)
1895  {
1896  ErrorLog(8, e.Message);
1897  }
1898 }
1899 
1900 // ---------------------------------------------------------------------------
1901 void __fastcall TInterface::ExitTrackButtonClick(TObject *Sender)
1902 {
1903  try
1904  {
1905  TrainController->LogEvent("ExitTrackButtonClick");
1906  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExitTrackButtonClick");
1907  ExitHeatmaps();
1908  if(Level2TrackMode == CutMoving)
1909  {
1910  Level2TrackMode = Pasting; // to paste the selection
1911  SetLevel2TrackMode(53);
1912  }
1913  DevelopmentPanel->Visible = false; // development use only
1914  ScreenGridFlag = false;
1915  SelectionValid = false;
1916  Track->SelectGraphicVector.clear();
1917  // delete all unwanted TPictures in UserGraphicMap
1918  if(!Track->UserGraphicMap.empty()) // if empty skip it
1919  {
1920  TTrack::TUserGraphicMap::iterator UGMIt = Track->UserGraphicMap.begin();
1921  do
1922  {
1923  bool GraphicFoundInVector = false;
1924  for(TTrack::TUserGraphicVector::iterator UGVIt = Track->UserGraphicVector.begin(); UGVIt < Track->UserGraphicVector.end(); UGVIt++)
1925  {
1926  if(UGMIt->first == UGVIt->FileName)
1927  {
1928  GraphicFoundInVector = true;
1929  break;
1930  }
1931  }
1932  if(!GraphicFoundInVector)
1933  {
1934  delete UGMIt->second;
1935  Track->UserGraphicMap.erase(UGMIt);
1936  UGMIt = Track->UserGraphicMap.begin(); // reset the iterator because erasing an element it points to invalidates it & if use it after then
1937  // behaviour is undefined, the iteration will end eventually because the map has got shorter after an erase
1938  }
1939  else
1940  {
1941  UGMIt++;
1942  }
1943  }
1944  while(UGMIt != Track->UserGraphicMap.end());
1945  }
1946  Level1Mode = BaseMode;
1947  SetLevel1Mode(2);
1948  Utilities->CallLogPop(1170);
1949  }
1950  catch(const Exception &e)
1951  {
1952  ErrorLog(129, e.Message);
1953  }
1954 }
1955 
1956 // ---------------------------------------------------------------------------
1957 void __fastcall TInterface::TextOrUserGraphicGridButtonClick(TObject *Sender)
1958 {
1959  try
1960  {
1961  TrainController->LogEvent("TextOrUserGraphicGridButtonClick," + AnsiString(TextOrUserGraphicGridVal));
1962  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TextOrUserGraphicGridButtonClick");
1963  if(TextOrUserGraphicGridVal == 1)
1964  {
1966  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision2");
1967  }
1968  else if(TextOrUserGraphicGridVal == 2)
1969  {
1971  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision4");
1972  }
1973  else if(TextOrUserGraphicGridVal == 4)
1974  {
1976  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision8");
1977  }
1978  else if(TextOrUserGraphicGridVal == 8)
1979  {
1981  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision16");
1982  }
1983  else
1984  {
1986  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision1");
1987  }
1988  Utilities->CallLogPop(1171);
1989  }
1990  catch(const Exception &e)
1991  {
1992  ErrorLog(130, e.Message);
1993  }
1994 }
1995 
1996 // ---------------------------------------------------------------------------
1997 void __fastcall TInterface::SigAspectButtonClick(TObject *Sender)
1998 {
1999  try
2000  {
2001  TrainController->LogEvent("SigAspectButtonClick," + AnsiString(Track->SignalAspectBuildMode));
2002  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SigAspectButtonClick");
2004  {
2006  SigAspectButton->Glyph->LoadFromResourceName(0, "ThreeAspect");
2007  }
2009  {
2011  SigAspectButton->Glyph->LoadFromResourceName(0, "TwoAspect");
2012  }
2014  {
2016  SigAspectButton->Glyph->LoadFromResourceName(0, "GroundSig");
2017 // set all signal glyphs to ground signals
2019  }
2020  else
2021  {
2023  SigAspectButton->Glyph->LoadFromResourceName(0, "FourAspect");
2024 // set all signal glyphs to normal signals
2026  }
2027  Utilities->CallLogPop(1869);
2028  }
2029  catch(const Exception &e)
2030  {
2031  ErrorLog(180, e.Message);
2032  }
2033 }
2034 
2035 // ---------------------------------------------------------------------------
2036 void __fastcall TInterface::FontButtonClick(TObject *Sender)
2037 {
2038  try
2039  {
2040  TrainController->LogEvent("FontButtonClick");
2041  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",FontButtonClick");
2042  FontDialog->Font = Display->GetFont(); // sets the dialog box font to the currently used font
2043  FontDialog->Execute(); // this displays the dialog box
2044  if(FontDialog->Font->Color == clB5G5R5) // white
2045  {
2046  FontDialog->Font->Color = clB0G0R0; // black - don't store white in font, will display black as white on dark backgrounds
2047  }
2048  Display->SetFont(FontDialog->Font); // sets the displayed font to the output from the dialog box
2049  if(TextBox->Visible)
2050  {
2051  TextBox->SetFocus();
2052  }
2053  else if(LocationNameTextBox->Visible)
2054  {
2055  LocationNameTextBox->SetFocus();
2056  }
2057  Utilities->CallLogPop(1172);
2058  }
2059  catch(const Exception &e)
2060  {
2061  ErrorLog(131, e.Message);
2062  }
2063 }
2064 
2065 // ---------------------------------------------------------------------------
2066 
2067 void __fastcall TInterface::ScreenGridButtonClick(TObject *Sender)
2068 {
2069  try
2070  {
2071  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ScreenGridButtonClick");
2072  if(ScreenGridFlag)
2073  {
2074  TrainController->LogEvent("ScreenGridButtonClick + ScreenGrid off");
2075  ScreenGridFlag = false;
2076  }
2077  else
2078  {
2079  TrainController->LogEvent("ScreenGridButtonClick + ScreenGrid on");
2080  ScreenGridFlag = true;
2081  }
2083  Utilities->CallLogPop(89);
2084  }
2085  catch(const Exception &e)
2086  {
2087  ErrorLog(33, e.Message);
2088  }
2089 }
2090 
2091 // ---------------------------------------------------------------------------
2092 // PrefDir Interface
2093 // ---------------------------------------------------------------------------
2094 void __fastcall TInterface::PlanPrefDirsMenuItemClick(TObject *Sender) // Mode Menu Item
2095 {
2096  try
2097  {
2098  TrainController->LogEvent("PlanPrefDirsMenuItemClick");
2099  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PlanPrefDirsMenuItemClick");
2101  SetLevel1Mode(3);
2102  Utilities->CallLogPop(1173);
2103  }
2104  catch(const Exception &e)
2105  {
2106  ErrorLog(132, e.Message);
2107  }
2108 }
2109 
2110 // ---------------------------------------------------------------------------
2111 void __fastcall TInterface::AddPrefDirButtonClick(TObject *Sender)
2112 {
2113  try
2114  {
2115  TrainController->LogEvent("AddPrefDirButtonClick");
2116  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddPrefDirButtonClick");
2117  if(ConstructPrefDir->PrefDirSize() == 0)
2118  {
2119  ShowMessage("No preferred direction selection");
2120  Utilities->CallLogPop(22);
2121  return;
2122  }
2123  Screen->Cursor = TCursor(-11); // Hourglass;
2125  {
2127  }
2129  SetLevel1Mode(4);
2130  Screen->Cursor = TCursor(-2); // Arrow
2131  Utilities->CallLogPop(23);
2132  }
2133  catch(const Exception &e)
2134  {
2135  ErrorLog(10, e.Message);
2136  }
2137 }
2138 
2139 // ---------------------------------------------------------------------------
2140 void __fastcall TInterface::DeleteAllPrefDirButtonClick(TObject *Sender)
2141 {
2142  try
2143  {
2144  TrainController->LogEvent("DeleteAllPrefDirButtonClick");
2145  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",DeleteAllPrefDirButtonClick");
2146  TMsgDlgButtons Buttons;
2147  Buttons << mbYes << mbNo;
2148  if(MessageDlg("Do you really want to clear all preferred directions?", mtWarning, Buttons, 0) == mrNo)
2149  {
2150  Utilities->CallLogPop(24);
2151  return;
2152  }
2153  // leave all as was before pressed DeleteAllPrefDirButton
2154  else
2155  {
2160  SetLevel1Mode(5);
2161  }
2162  Utilities->CallLogPop(25);
2163  }
2164  catch(const Exception &e)
2165  {
2166  ErrorLog(11, e.Message);
2167  }
2168 }
2169 // ---------------------------------------------------------------------------
2170 
2171 void __fastcall TInterface::DeleteOnePrefDirButtonClick(TObject *Sender)
2172 {
2173  try
2174  {
2175  TrainController->LogEvent("DeleteOnePrefDirButtonClick");
2176  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",DeleteOnePrefDirButtonClick");
2177  ResetChangedFileDataAndCaption(18, false);
2178 // RlyFile = false; - don't alter this just for PrefDir changes
2179  Screen->Cursor = TCursor(-11); // Hourglass;
2180  for(unsigned int x = 0; x < ConstructPrefDir->PrefDirSize(); x++)
2181  {
2184  }
2187  SetLevel1Mode(81); // all PrefDir truncated
2188  Screen->Cursor = TCursor(-2); // Arrow
2189  Utilities->CallLogPop(1591);
2190  }
2191  catch(const Exception &e)
2192  {
2193  ErrorLog(46, e.Message);
2194  }
2195 }
2196 
2197 // ---------------------------------------------------------------------------
2198 
2199 void __fastcall TInterface::ExitPrefDirButtonClick(TObject *Sender)
2200 {
2201  try
2202  {
2203  TrainController->LogEvent("ExitPrefDirButtonClick");
2204  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExitPrefDirButtonClick");
2205  Level1Mode = BaseMode;
2206  SetLevel1Mode(6);
2207  Utilities->CallLogPop(1554);
2208  }
2209  catch(const Exception &e)
2210  {
2211  ErrorLog(133, e.Message);
2212  }
2213 }
2214 
2215 // ---------------------------------------------------------------------------
2216 // Operate Railway Interface
2217 // ---------------------------------------------------------------------------
2218 void __fastcall TInterface::OperateRailwayMenuItemClick(TObject *Sender) // Mode Menu Item
2219 {
2220  try
2221  {
2222  TrainController->LogEvent("OperateRailwayMenuItemClick");
2223  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",OperateRailwayMenuItemClick");
2224  TTrain::NextTrainID = 0; // reset to 0 whenever enter operating mode
2225  AllRoutes->NextRouteID = 0; // reset to 0 whenever enter operating mode
2226  Level1Mode = OperMode;
2227  SetLevel1Mode(7);
2228  Utilities->CallLogPop(26);
2229  }
2230  catch(const Exception &e)
2231  {
2232  ErrorLog(12, e.Message);
2233  }
2234 }
2235 
2236 // ---------------------------------------------------------------------------
2237 void __fastcall TInterface::OperateButtonClick(TObject *Sender)
2238 {
2239  try
2240  {
2241  TrainController->LogEvent("StartOperationButtonClick");
2242  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",OperateButtonClick");
2244  {
2246  SetLevel2OperMode(0);
2247  }
2248  else
2249  {
2251  SetLevel2OperMode(1);
2252  }
2253  Utilities->CallLogPop(1175);
2254  }
2255  catch(const Exception &e)
2256  {
2257  ErrorLog(37, e.Message);
2258  }
2259 }
2260 
2261 // ---------------------------------------------------------------------------
2262 void __fastcall TInterface::AutoSigsButtonClick(TObject *Sender)
2263 // must have PrefDirs to be available
2264 {
2265  try
2266  {
2267  TrainController->LogEvent("AutoSigsButtonClick");
2268  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AutoSigsButtonClick");
2269  AutoSigsFlag = true;
2270  PreferredRoute = true;
2271  ConsecSignalsRoute = true;
2272 
2273  AutoSigsButton->Enabled = false;
2274  SigAutoNonConsecButton->Enabled = true;
2275  SigPrefConsecButton->Enabled = true;
2276  SigPrefNonConsecButton->Enabled = true;
2277  UnrestrictedButton->Enabled = true;
2278 
2279  InfoPanel->Visible = true;
2280  if(Level2OperMode == PreStart)
2281  {
2282  InfoPanel->Caption = "PRE-START: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
2283  }
2284  else
2285  {
2286  InfoPanel->Caption = "OPERATING: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
2287  }
2288  InfoCaptionStore = InfoPanel->Caption;
2289  AutoRouteStartMarker->PlotOriginal(1, Display); // if overlay not plotted will ignore
2290  SigRouteStartMarker->PlotOriginal(2, Display); // if overlay not plotted will ignore
2291  NonSigRouteStartMarker->PlotOriginal(3, Display); // if overlay not plotted will ignore
2293  Utilities->CallLogPop(28);
2294  }
2295  catch(const Exception &e)
2296  {
2297  ErrorLog(14, e.Message);
2298  }
2299 }
2300 
2301 // ---------------------------------------------------------------------------
2302 
2303 void __fastcall TInterface::SigAutoNonConsecButtonClick(TObject *Sender)
2304 // must have PrefDirs to be available
2305 {
2306  try
2307  {
2308  TrainController->LogEvent("SigAutoNonConsecButtonClick");
2309  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SigAutoNonConsecButtonClick");
2310  AutoSigsFlag = true;
2311  PreferredRoute = true;
2312  ConsecSignalsRoute = false;
2313 
2314  AutoSigsButton->Enabled = true;
2315  SigAutoNonConsecButton->Enabled = false;
2316  SigPrefConsecButton->Enabled = true;
2317  SigPrefNonConsecButton->Enabled = true;
2318  UnrestrictedButton->Enabled = true;
2319 
2320  InfoPanel->Visible = true;
2321  if(Level2OperMode == PreStart)
2322  {
2323  InfoPanel->Caption = "PRE-START: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
2324  }
2325  else
2326  {
2327  InfoPanel->Caption = "OPERATING: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
2328  }
2329  InfoCaptionStore = InfoPanel->Caption;
2330  AutoRouteStartMarker->PlotOriginal(46, Display); // if overlay not plotted will ignore
2331  SigRouteStartMarker->PlotOriginal(47, Display); // if overlay not plotted will ignore
2332  NonSigRouteStartMarker->PlotOriginal(48, Display); // if overlay not plotted will ignore
2334  Utilities->CallLogPop(2556);
2335  }
2336  catch(const Exception &e)
2337  {
2338  ErrorLog(247, e.Message);
2339  }
2340 }
2341 
2342 //---------------------------------------------------------------------------
2343 
2344 void __fastcall TInterface::SigPrefConsecButtonClick(TObject *Sender)
2345 // must have PrefDirs to be available
2346 {
2347  try
2348  {
2349  TrainController->LogEvent("SigPrefConsecButtonClick");
2350  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SigPrefConsecButtonClick");
2351  AutoSigsFlag = false;
2352  PreferredRoute = true;
2353  ConsecSignalsRoute = true;
2354 
2355  AutoSigsButton->Enabled = true;
2356  SigAutoNonConsecButton->Enabled = true;
2357  SigPrefConsecButton->Enabled = false;
2358  SigPrefNonConsecButton->Enabled = true;
2359  UnrestrictedButton->Enabled = true;
2360 
2361  InfoPanel->Visible = true;
2362  if(Level2OperMode == PreStart)
2363  {
2364  InfoPanel->Caption = "PRE-START: Select PREFERRED ROUTE start signal, or left click points to change manually";
2365  }
2366  else
2367  {
2368  InfoPanel->Caption = "OPERATING: Select PREFERRED ROUTE start signal, or left click points to change manually";
2369  }
2370  InfoCaptionStore = InfoPanel->Caption;
2371  AutoRouteStartMarker->PlotOriginal(43, Display); // if overlay not plotted will ignore
2372  SigRouteStartMarker->PlotOriginal(44, Display); // if overlay not plotted will ignore
2373  NonSigRouteStartMarker->PlotOriginal(45, Display); // if overlay not plotted will ignore
2375  Utilities->CallLogPop(2265);
2376  }
2377  catch(const Exception &e)
2378  {
2379  ErrorLog(221, e.Message);
2380  }
2381 }
2382 
2383 // ---------------------------------------------------------------------------
2384 
2385 void __fastcall TInterface::SigPrefNonConsecButtonClick(TObject *Sender)
2386 // must have PrefDirs to be available
2387 {
2388  try
2389  {
2390  TrainController->LogEvent("SigPrefNonConsecButtonClick");
2391  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SigPrefNonConsecButtonClick");
2392  AutoSigsFlag = false;
2393  PreferredRoute = true;
2394  ConsecSignalsRoute = false;
2395 
2396  AutoSigsButton->Enabled = true;
2397  SigAutoNonConsecButton->Enabled = true;
2398  SigPrefConsecButton->Enabled = true;
2399  SigPrefNonConsecButton->Enabled = false;
2400  UnrestrictedButton->Enabled = true;
2401 
2402  InfoPanel->Visible = true;
2403  if(Level2OperMode == PreStart)
2404  {
2405  InfoPanel->Caption = "PRE-START: Select PREFERRED ROUTE start signal, or left click points to change manually";
2406  }
2407  else
2408  {
2409  InfoPanel->Caption = "OPERATING: Select PREFERRED ROUTE start signal, or left click points to change manually";
2410  }
2411  InfoCaptionStore = InfoPanel->Caption;
2412  AutoRouteStartMarker->PlotOriginal(4, Display); // if overlay not plotted will ignore
2413  SigRouteStartMarker->PlotOriginal(5, Display); // if overlay not plotted will ignore
2414  NonSigRouteStartMarker->PlotOriginal(6, Display); // if overlay not plotted will ignore
2416  Utilities->CallLogPop(29);
2417  }
2418  catch(const Exception &e)
2419  {
2420  ErrorLog(15, e.Message);
2421  }
2422 }
2423 
2424 // ---------------------------------------------------------------------------
2425 void __fastcall TInterface::UnrestrictedButtonClick(TObject *Sender)
2426 {
2427  try
2428  {
2429  TrainController->LogEvent("UnrestrictedButtonClick");
2430  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",UnrestrictedButtonClick");
2431  AutoSigsFlag = false;
2432  PreferredRoute = false;
2433  ConsecSignalsRoute = false;
2434  if(EveryPrefDir->PrefDirSize() > 0)
2435  {
2436  AutoSigsButton->Enabled = true;
2437  SigAutoNonConsecButton->Enabled = true;
2438  SigPrefConsecButton->Enabled = true;
2439  SigPrefNonConsecButton->Enabled = true;
2440  UnrestrictedButton->Enabled = false;
2441  }
2442  else
2443  {
2444  AutoSigsButton->Enabled = false;
2445  SigAutoNonConsecButton->Enabled = false;
2446  SigPrefConsecButton->Enabled = false;
2447  SigPrefNonConsecButton->Enabled = false;
2448  UnrestrictedButton->Enabled = false;
2449  }
2450  InfoPanel->Visible = true;
2451  if(Level2OperMode == PreStart)
2452  {
2453  InfoPanel->Caption = "PRE-START: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
2454  }
2455  else
2456  {
2457  InfoPanel->Caption = "OPERATING: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
2458  }
2459  InfoCaptionStore = InfoPanel->Caption;
2460  AutoRouteStartMarker->PlotOriginal(7, Display); // if overlay not plotted will ignore
2461  SigRouteStartMarker->PlotOriginal(8, Display); // if overlay not plotted will ignore
2462  NonSigRouteStartMarker->PlotOriginal(9, Display); // if overlay not plotted will ignore
2464  Utilities->CallLogPop(30);
2465  }
2466  catch(const Exception &e)
2467  {
2468  ErrorLog(16, e.Message);
2469  }
2470 }
2471 
2472 // ---------------------------------------------------------------------------
2473 void __fastcall TInterface::RouteCancelButtonClick(TObject *Sender)
2474 {
2475  try
2476  {
2477  TrainController->LogEvent("RouteCancelButtonClick");
2478  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RouteCancelButtonClick");
2479  RouteCancelFlag = true;
2480  InfoPanel->Visible = true;
2481  InfoPanel->Caption = "ROUTE CANCELLING: Right click on truncate element, first element to cancel (anywhere else to skip)";
2482  RouteCancelButton->Enabled = false;
2483  AutoRouteStartMarker->PlotOriginal(32, Display); // if overlay not plotted will ignore
2484  SigRouteStartMarker->PlotOriginal(33, Display); // if overlay not plotted will ignore
2485  NonSigRouteStartMarker->PlotOriginal(34, Display); // if overlay not plotted will ignore
2486  Utilities->CallLogPop(1176);
2487  }
2488  catch(const Exception &e)
2489  {
2490  ErrorLog(35, e.Message);
2491  }
2492 }
2493 
2494 // ---------------------------------------------------------------------------
2495 void __fastcall TInterface::PerformanceLogButtonClick(TObject *Sender)
2496 {
2497  try
2498  {
2499  TrainController->LogEvent("PerformanceLogButtonClick");
2500  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PerformanceLogButtonClick");
2501  if(!ShowPerfLogForm) //changed at v2.13.0 for Performance log form, so starts on bottom LH corner on first startup
2502  //but retains current position thereafter
2503  {
2504  ShowPerfLogForm = true;
2505  PerfLogForm->Visible = true;
2506  if(FirstPerfLogFormDisplay) //initial position of PerfLogForm
2507  {
2508  PerfLogForm->Top = Screen->Height - PerfLogForm->Height - 32; //-32 to avoid overlapping taskbar
2509  PerfLogForm->Left = 0;
2510  FirstPerfLogFormDisplay = false;
2511  }
2512  PerformanceLogButton->Glyph->LoadFromResourceName(0, "HideLog");
2513 
2514  }
2515  else
2516  {
2517  ShowPerfLogForm = false;
2518  PerfLogForm->Visible = false;
2519  PerformanceLogButton->Glyph->LoadFromResourceName(0, "ShowLog");
2520  }
2521  Utilities->CallLogPop(1177);
2522  }
2523  catch(const Exception &e)
2524  {
2525  ErrorLog(36, e.Message);
2526  }
2527 }
2528 // ---------------------------------------------------------------------------
2529 
2530 void __fastcall TInterface::OperatorActionButtonClick(TObject *Sender)
2531 {
2532  try
2533  {
2534  TrainController->LogEvent("OperatorActionButtonClick");
2535  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",OperatorActionButtonClick");
2536  if(!ShowActionsDueForm)
2537  {
2538  ShowActionsDueForm = true;
2539  if(FirstActionsDueFormDisplay) //initial position of PerfLogForm
2540  {
2541  ActionsDueForm->Top = Screen->Height - ActionsDueForm->Height - 32; //-32 to avoid overlapping taskbar;
2542  ActionsDueForm->Left = Screen->Width - ActionsDueForm->Width;
2544  }
2545  else
2546  {
2547  ActionsDueForm->Top = ADFTop;
2548  ActionsDueForm->Left = ADFLeft;
2549  }
2550  ActionsDueForm->Visible = true;
2552  OperatorActionButton->Glyph->LoadFromResourceName(0, "HideOpActionPanel");
2553  }
2554  else
2555  {
2556  ShowActionsDueForm = false;
2557  ActionsDueForm->Visible = false;
2559  OperatorActionButton->Glyph->LoadFromResourceName(0, "ShowOpActionPanel");
2560  }
2561  Utilities->CallLogPop(2073);
2562  }
2563  catch(const Exception &e)
2564  {
2565  ErrorLog(199, e.Message);
2566  }
2567 }
2568 
2569 // ---------------------------------------------------------------------------
2570 
2571 void __fastcall TInterface::ExitOperationButtonClick(TObject *Sender)
2572 {
2573  try
2574  {
2575  TrainController->LogEvent("ExitOperationButtonClick");
2576  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExitOperationButtonClick");
2578  {
2579  UnicodeString MessageStr = "Please note that the session will be lost if it hasn't been saved. Do you still wish to exit?";
2580  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
2582  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
2583  TrainController->BaseTime = TDateTime::CurrentDateTime();
2585  if(button == IDNO)
2586  {
2587  Utilities->CallLogPop(751);
2588  return;
2589  }
2590  }
2591  Track->ResetSignals(0);
2592  Track->ResetPoints(0);
2593  Track->ResetTSRs(0); //added at v2.14.0
2594  TrainController->SendPerformanceSummary(0, Utilities->PerformanceFile); // must come before trains finished becuase examines the train vectors
2595  Utilities->PerformanceFile.close();
2598  RouteMode = None;
2599  PreferredRoute = true; // default starting conditions
2600  ConsecSignalsRoute = true; // default starting conditions
2602  ShowPerfLogForm = false;
2603  PerformanceLogButton->Glyph->LoadFromResourceName(0, "ShowLog");
2604  ShowActionsDueForm = false; // new at v2.2.0
2605  OperatorActionButton->Glyph->LoadFromResourceName(0, "ShowOpActionPanel"); // new v2.2.0
2606  PerfLogForm->PerformanceLogBox->Lines->Clear();
2607  PerfLogForm->Visible = false;
2608 // TipButton->Glyph->LoadFromResourceName(0, "ShowLog"); //'Trains in play' new at v2.2.0
2610  ActionsDueForm->Visible = false;
2611  ;
2613  AllRoutes->LockedRouteVector.clear();
2614  Level1Mode = BaseMode;
2615  SetLevel1Mode(8); // calls Clearand...
2616  Utilities->CallLogPop(1555);
2617  }
2618  catch(const Exception &e)
2619  {
2620  ErrorLog(13, e.Message);
2621  }
2622 }
2623 
2624 // ---------------------------------------------------------------------------
2625 // Menu Interface (for items not already covered above)
2626 // ---------------------------------------------------------------------------
2627 void __fastcall TInterface::LoadRailwayMenuItemClick(TObject* Sender)
2628 {
2629  try
2630  {
2631  TrainController->LogEvent("LoadRailwayMenuItemClick");
2632  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LoadRailwayMenuItemClick");
2633  // LoadRailwayDialog->Filter = "Development file (*.dev)|*.dev|Railway file (*.rly)|*.rly"; //as was
2634  // changed at v2.0.0 (Embarcadero change) to show all files together
2635  LoadRailwayDialog->Filter = "Railway file (*.rly or *.dev)|*.rly; *.dev";
2636  if (LoadRailwayDialog->Execute())
2637  {
2638  if (LoadRailwayDialog->InitialDir != TPath::GetDirectoryName(LoadRailwayDialog->FileName)) // new at v2.6.0 to retain a new directory
2639  {
2640  LoadRailwayDialog->InitialDir = TPath::GetDirectoryName(LoadRailwayDialog->FileName);
2641  SaveRailwayDialog->InitialDir = TPath::GetDirectoryName(LoadRailwayDialog->FileName);
2642  }
2643  if (!ClearEverything(1)) //moved here from above ...->Execute... at v2.20.0 so the loaded railway isn't removed if the load doesn't go ahead (Stephen Smith
2644  { //pull requ merged 04/04/24)
2645  Utilities->CallLogPop(1139);
2646  return;
2647  }
2648  TrainController->LogEvent("LoadRailway " + AnsiString(LoadRailwayDialog->FileName));
2649  LoadRailway(0, AnsiString(LoadRailwayDialog->FileName));
2650  Track->CalcHLocMinEtc(9);
2651  Level1Mode = BaseMode;
2654  SetLevel1Mode(11); // calls Clearand... to plot the new railway
2655  Utilities->CallLogPop(31);
2656  }
2657 // else ShowMessage("Load Safely Aborted"); //dropped at v2.20.0, not needed
2658  }
2659  catch (const Exception &e) //made a non-error catch at v2.14.0 following Albie Vowles error of 15/12/22
2660  {
2661  TrainController->StopTTClockMessage(134, "Railway file failed to load - is the latest program version in use?\nIf so then the file may be corrupt.\n\nError message: " + e.Message);
2662  Screen->Cursor = TCursor(-2); // Arrow;
2663  Utilities->CallLogPop(2551);
2664  // ErrorLog(17, e.Message);
2665  }
2666 }
2667 
2668 // ---------------------------------------------------------------------------
2669 
2670 void TInterface::LoadRailway(int Caller, AnsiString LoadFileName)
2671 {
2672  // display of the loaded railway covered in the calling routine
2673  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadRailway," + LoadFileName);
2674  if(FileIntegrityCheck(0, LoadFileName.c_str()))
2675  {
2676  Screen->Cursor = TCursor(-11); // Hourglass;
2677  std::ifstream VecFile(LoadFileName.c_str());
2678  if(!(VecFile.fail()))
2679  {
2680  AnsiString TempString = Utilities->LoadFileString(VecFile); // version number
2681  int TempOffsetHHome = Utilities->LoadFileInt(VecFile);
2682  int TempOffsetVHome = Utilities->LoadFileInt(VecFile);
2683  bool GraphicsFollow = false;
2684  // can't load DisplayOffsetH & VHome until after LoadTrack as that calls TrackClear & zeroes them
2685 // load track elements
2686  Track->LoadTrack(1, VecFile, GraphicsFollow);
2687 // load text elements
2688  TextHandler->LoadText(0, VecFile);
2689 // load PrefDir elements
2690  EveryPrefDir->LoadPrefDir(0, VecFile);
2691  if(GraphicsFollow)
2692  {
2693 // load user graphics
2694  Track->LoadGraphics(0, VecFile, CurDir + "\\" + USERGRAPHICS_DIR_NAME); // include path to Graphics folder
2695  }
2696  EveryPrefDir->CheckPrefDirAgainstTrackVector(0); // clears PrefDir if any discrepancies found
2697  VecFile.close();
2698  Display->DisplayOffsetHHome = TempOffsetHHome;
2699  Display->DisplayOffsetVHome = TempOffsetVHome;
2701 
2702  TFont *TempFont = new TFont; // if try to alter MainScreen->Canvas->Font directly it won't change the style for some reason
2703  TempFont->Style.Clear();
2704  TempFont->Name = "MS Sans Serif"; // reset font, else stays set to last displayed text font
2705  TempFont->Size = 10;
2706  TempFont->Color = clB0G0R0;
2707  TempFont->Charset = (TFontCharset)(0);
2708  MainScreen->Canvas->Font->Assign(TempFont);
2709  delete TempFont;
2710 
2711 // calculate starting zoomed out offset values - same as when zoom out button clicked
2712  int OVOffH_NVCentre = Display->DisplayOffsetH - (1.5 * Utilities->ScreenElementWidth);
2713 // start zoomout centre at DisplayOffsetH + 30 - zoomout width/2 = -(1.5 * 60)
2714  int LeftExcess = OVOffH_NVCentre - Track->GetHLocMin();
2715  int RightExcess = Track->GetHLocMax() - OVOffH_NVCentre - ((4 * Utilities->ScreenElementWidth) - 1);
2716  if((LeftExcess > 0) && (RightExcess > 0))
2717  {
2718  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre;
2719  }
2720  else if((LeftExcess > 0) && (RightExcess <= 0))
2721  {
2722  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre + ((RightExcess) / (Utilities->ScreenElementWidth / 2)) *
2723  (Utilities->ScreenElementWidth / 2); // normalise to nearest half screen
2724  }
2725  else if((LeftExcess <= 0) && (RightExcess > 0))
2726  {
2727  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre - ((LeftExcess) / (Utilities->ScreenElementWidth / 2)) * (Utilities->ScreenElementWidth / 2);
2728  }
2729  else
2730  {
2731  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre; // no excess at either side, so display in centre
2732 
2733  }
2734  int OVOffV_NVCentre = Display->DisplayOffsetV - (1.5 * Utilities->ScreenElementHeight);
2735  int TopExcess = OVOffV_NVCentre - Track->GetVLocMin();
2736  int BotExcess = Track->GetVLocMax() - OVOffV_NVCentre - ((4 * Utilities->ScreenElementHeight) - 1);
2737  if((TopExcess > 0) && (BotExcess > 0))
2738  {
2739  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre;
2740  }
2741  else if((TopExcess > 0) && (BotExcess <= 0))
2742  {
2743  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre + ((BotExcess) / (Utilities->ScreenElementHeight / 2)) *
2744  (Utilities->ScreenElementHeight / 2); // normalise to nearest half screen
2745  }
2746  else if((TopExcess <= 0) && (BotExcess > 0))
2747  {
2748  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre - ((TopExcess) / (Utilities->ScreenElementHeight / 2)) * (Utilities->ScreenElementHeight / 2);
2749  }
2750  else
2751  {
2752  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre; // no excess at either side, so display in centre
2753  }
2754 // all above same as when zoom out button clicked
2755  Display->DisplayZoomOutOffsetVHome = Display->DisplayZoomOutOffsetV; // now set zoomed out 'home' values
2757 
2758  SavedFileName = AnsiString(LoadRailwayDialog->FileName); // includes the full PrefDir
2759  if(SavedFileName != "") // shouldn't be "" at this stage but leave in as a safeguard
2760  {
2761  Track->DuplicatedLocationName(1, true);
2762  char LastChar = SavedFileName[SavedFileName.Length()];
2763  if((LastChar == 'y') || (LastChar == 'Y'))
2764  {
2765  if(!(Track->IsReadyForOperation(false)))
2766  {
2767  ShowMessage("Railway not ready for operation so unable to load as a .rly file. Loading as a new railway under development");
2768  SavedFileName = "";
2769  RlyFile = false;
2770  RailwayTitle = "";
2771  TimetableTitle = "";
2772  SetCaption(5);
2773  Track->CalcHLocMinEtc(1);
2774  Screen->Cursor = TCursor(-2); // Arrow
2775  Level1Mode = BaseMode;
2776  SetLevel1Mode(9);
2777  session_api_->dump(); // update session INI file //added at v2.10.0
2778  Utilities->CallLogPop(1136);
2779  return;
2780  }
2781  else
2782  {
2783  RlyFile = true;
2784  }
2785  }
2786  else
2787  {
2788  RlyFile = false;
2789  }
2790  }
2791  else
2792  {
2793  RlyFile = false;
2794  }
2795  FileChangedFlag = false;
2796  for(int x = AnsiString(LoadRailwayDialog->FileName).Length(); x > 0; x--)
2797  {
2798  if(AnsiString(LoadRailwayDialog->FileName)[x] == '\\')
2799  {
2800  RailwayTitle = AnsiString(LoadRailwayDialog->FileName).SubString(x + 1, AnsiString(LoadRailwayDialog->FileName).Length() - x - 4);
2801  TimetableTitle = "";
2802  SetCaption(6);
2803  break;
2804  }
2805  }
2806  } // if(VecFile)
2807  else
2808  {
2809  ShowMessage("File open failed prior to load");
2810  }
2811  Screen->Cursor = TCursor(-2); // Arrow
2812  } // if(FileIntegrityCheck(LoadRailwayDialog->FileName.c_str()))
2813  else
2814  {
2815  ShowMessage("File integrity check failed - unable to load " + LoadFileName + ". Retry using the latest program version. If that fails and the file exists and is spelled correctly then it is probably corrupt.");
2816  } //message clarified at v2.14.0
2817  session_api_->write_string("railway_file", LoadRailwayDialog->FileName); // API v1.2
2818  session_api_->find_metadata_file(); // API v1.2
2819  session_api_->dump(); // update session INI file //added at v2.10.0
2820  Utilities->CallLogPop(1774);
2821 }
2822 
2823 // ---------------------------------------------------------------------------
2824 
2825 void __fastcall TInterface::SaveMenuItemClick(TObject *Sender)
2826 {
2827 // save under existing name
2828 // no need to alter RlyFile for saving under existing name
2829 
2830  try
2831  {
2832  TrainController->LogEvent("SaveMenuItemClick, " + SavedFileName);
2833  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveMenuItemClick");
2834  Screen->Cursor = TCursor(-11); // Hourglass;
2835  std::ofstream VecFile(SavedFileName.c_str());
2836  if(!(VecFile.fail()))
2837  {
2841  // save track elements
2842  if(Track->UserGraphicVector.empty())
2843  {
2844  Track->SaveTrack(3, VecFile, false); // false for no graphics (**Active elements** saved as marker)
2845  }
2846  else
2847  {
2848  Track->SaveTrack(8, VecFile, true); // true for graphics to be saved (**Active elements**1 saved as marker)
2849  }
2850  // save text elements
2851  TextHandler->SaveText(0, VecFile);
2852  // save PrefDir elements
2853  EveryPrefDir->SavePrefDirVector(0, VecFile);
2854  if(!Track->UserGraphicVector.empty())
2855  {
2856  // save user graphics
2857  Track->SaveUserGraphics(0, VecFile);
2858  }
2859  FileChangedFlag = false;
2860  VecFile.close();
2861  }
2862  else
2863  {
2864  ShowMessage("File open failed prior to save");
2865  }
2866  Screen->Cursor = TCursor(-2); // Arrow
2867  Level1Mode = BaseMode;
2868  SetLevel1Mode(12); // to disable the save option
2869  Utilities->CallLogPop(1178);
2870  }
2871  catch(const Exception &e)
2872  {
2873  ErrorLog(135, e.Message);
2874  }
2875 }
2876 
2877 // ---------------------------------------------------------------------------
2878 void __fastcall TInterface::SaveAsMenuItemClick(TObject *Sender)
2879 {
2880  try
2881  {
2882  TrainController->LogEvent("SaveAsMenuItemClick");
2883  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveAsMenuItemClick");
2884  SaveAsSubroutine(0);
2885  Utilities->CallLogPop(32);
2886  }
2887  catch(const Exception &e) //non-error catch added at v2.14.0
2888  {
2889  Screen->Cursor = TCursor(-2); // Arrow;
2890  UnicodeString MessageStr = "Unable to save\nError message: " + e.Message;
2891  Utilities->CallLogPop(2552);
2892 // ErrorLog(18, e.Message);
2893  }
2894 }
2895 
2896 // ---------------------------------------------------------------------------
2897 
2898 void __fastcall TInterface::SaveImageNoGridMenuItemClick(TObject *Sender)
2899 {
2900  // need to stop clock in case invoke during operation
2901  try
2902  {
2903  TrainController->LogEvent("SaveImageNoGridMenuItemClick");
2904  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveImageNoGridMenuItemClick");
2905  if(!DirectoryExists(CurDir + "\\" + IMAGE_DIR_NAME))
2906  {
2907  ShowMessage("Failed to find folder " + IMAGE_DIR_NAME + " in the folder where 'railway.exe' resides. Image can't be saved");
2908  Utilities->CallLogPop(1695);
2909  return;
2910  }
2911  Screen->Cursor = TCursor(-11); // Hourglass;
2912  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
2914  AnsiString ImageFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
2915  // format "16/06/2009 20:55:17"
2916  // avoid characters in filename:= / \ : * ? " < > |
2917  ImageFileName = CurDir + "\\" + IMAGE_DIR_NAME + "\\RailwayImage " + ImageFileName + "; " + RailwayTitle + ".bmp";
2918  AnsiString ShortName = "";
2919  for(int x = ImageFileName.Length(); x > 0; x--)
2920  {
2921  if(ImageFileName[x] == '\\')
2922  {
2923  ShortName = ImageFileName.SubString(x + 1, ImageFileName.Length() - x - 4);
2924  break;
2925  }
2926  }
2927  ShowMessage("A bitmap file named " + ShortName + " will be created in the Images folder");
2928  Graphics::TBitmap *RailwayImage = new Graphics::TBitmap;
2929  RailwayImage->PixelFormat = pf8bit; // needed to ensure compatibility with track
2930 
2931  int HPosMin = Track->GetHLocMin() * 16;
2932  int HPosMax = (Track->GetHLocMax() + 1) * 16;
2933  int VPosMin = Track->GetVLocMin() * 16;
2934  int VPosMax = (Track->GetVLocMax() + 1) * 16;
2935  RailwayImage->Width = HPosMax - HPosMin;
2936  RailwayImage->Height = VPosMax - VPosMin;
2937 
2938  // need to check if there is any text that extends past HPosMax or below VPosMax
2939  // use font height x text length x .7 for new rh calc & font height * 1.5 for new bottom calc
2940  if(!TextHandler->TextVector.empty())
2941  {
2942  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr != TextHandler->TextVector.end(); TextPtr++)
2943  {
2944  int NewWidth = (TextPtr->HPos - HPosMin) + (abs(TextPtr->Font->Height) * TextPtr->TextString.Length() * 0.7);
2945  int NewHeight = (TextPtr->VPos - VPosMin) + (abs(TextPtr->Font->Height) * 1.5);
2946  if(NewWidth > RailwayImage->Width)
2947  {
2948  RailwayImage->Width = NewWidth;
2949  }
2950  if(NewHeight > RailwayImage->Height)
2951  {
2952  RailwayImage->Height = NewHeight;
2953  }
2954  }
2955  }
2956  RailwayImage->Canvas->Brush->Color = clB5G5R5; // set it all to white initially
2957  TRect Rect(0, 0, RailwayImage->Width, RailwayImage->Height);
2958  RailwayImage->Canvas->FillRect(Rect);
2959 
2960  // write graphics first so text & track overwrite
2961  Track->WriteGraphicsToImage(0, RailwayImage);
2962  // then write track & text so text overwrites graphics & inactive elements //changed name & added text after inactives at v2.10.0
2963  Track->WriteTrackAndTextToImage(0, RailwayImage);
2964 
2965  RailwayImage->SaveToFile(ImageFileName);
2966  delete RailwayImage;
2967  TrainController->BaseTime = TDateTime::CurrentDateTime();
2969  Screen->Cursor = TCursor(-2); // Arrow
2970  Utilities->CallLogPop(1535);
2971  }
2972  catch(const Exception &e) //non-error catch //modified at v2.13.0 to catch all errors
2973  {
2974  Screen->Cursor = TCursor(-2); // Arrow;
2975  UnicodeString MessageStr = "Unable to write the image to file, it may be too big";
2976  Application->MessageBox(MessageStr.c_str(), L"", MB_OK | MB_ICONWARNING);
2977  Utilities->CallLogPop(2297);
2978  }
2979 }
2980 
2981 // ---------------------------------------------------------------------------
2982 
2983 void __fastcall TInterface::SaveImageAndGridMenuItemClick(TObject *Sender)
2984 {
2985  // need to stop clock in case invoke during operation
2986  try
2987  {
2988  TrainController->LogEvent("SaveImageAndGridMenuItemClick");
2989  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveImageAndGridMenuItemClick");
2990  if(!DirectoryExists(CurDir + "\\" + IMAGE_DIR_NAME))
2991  {
2992  ShowMessage("Failed to find folder " + IMAGE_DIR_NAME + " in the folder where 'railway.exe' resides. Image can't be saved");
2993  Utilities->CallLogPop(1696);
2994  return;
2995  }
2996  Screen->Cursor = TCursor(-11); // Hourglass;
2997  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
2999  AnsiString ImageFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
3000  // format "16/06/2009 20:55:17"
3001  // avoid characters in filename:= / \ : * ? " < > |
3002  ImageFileName = CurDir + "\\" + IMAGE_DIR_NAME + "\\RailwayImage " + ImageFileName + "; " + RailwayTitle + ".bmp";
3003  AnsiString ShortName = "";
3004  for(int x = ImageFileName.Length(); x > 0; x--)
3005  {
3006  if(ImageFileName[x] == '\\')
3007  {
3008  ShortName = ImageFileName.SubString(x + 1, ImageFileName.Length() - x - 4);
3009  break;
3010  }
3011  }
3012  ShowMessage("A bitmap file named " + ShortName + " will be created in the Images folder");
3013  Graphics::TBitmap *RailwayImage = new Graphics::TBitmap;
3014  RailwayImage->PixelFormat = pf8bit; // needed to ensure compatibility with track
3015  int HPosMin = Track->GetHLocMin() * 16;
3016  int HPosMax = (Track->GetHLocMax() + 1) * 16;
3017  int VPosMin = Track->GetVLocMin() * 16;
3018  int VPosMax = (Track->GetVLocMax() + 1) * 16;
3019  RailwayImage->Width = HPosMax - HPosMin;
3020  RailwayImage->Height = VPosMax - VPosMin;
3021 
3022  // need to check if there is any text that extends past HPosMax or below VPosMax
3023  // use font height x text length x .7 for new rh calc & font height * 1.5 for new bottom calc
3024  if(!TextHandler->TextVector.empty())
3025  {
3026  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr != TextHandler->TextVector.end(); TextPtr++)
3027  {
3028  int NewWidth = (TextPtr->HPos - HPosMin) + (abs(TextPtr->Font->Height) * TextPtr->TextString.Length() * 0.7);
3029  int NewHeight = (TextPtr->VPos - VPosMin) + (abs(TextPtr->Font->Height) * 1.5);
3030  if(NewWidth > RailwayImage->Width)
3031  {
3032  RailwayImage->Width = NewWidth;
3033  }
3034  if(NewHeight > RailwayImage->Height)
3035  {
3036  RailwayImage->Height = NewHeight;
3037  }
3038  }
3039  }
3040  RailwayImage->Canvas->Brush->Color = clB5G5R5; // set it all to white initially
3041  TRect Rect(0, 0, RailwayImage->Width, RailwayImage->Height);
3042  RailwayImage->Canvas->FillRect(Rect);
3043 
3044  // write the grid first so all else on top
3045  for(int x = 0; x < ((RailwayImage->Width) / 16); x++)
3046  {
3047  for(int y = 0; y < ((RailwayImage->Height) / 16); y++)
3048  {
3049  RailwayImage->Canvas->Draw((x * 16), (y * 16), RailGraphics->bmGrid); // graphic is black on white so no need to change
3050  }
3051  }
3052  // write graphics next so text & track overwrite
3053  Track->WriteGraphicsToImage(1, RailwayImage);
3054  // then write track & text so text overwrites graphics & inactive elements
3055  Track->WriteTrackAndTextToImage(1, RailwayImage); //changed name & added text after inactives at v2.10.0
3056  RailwayImage->SaveToFile(ImageFileName);
3057  delete RailwayImage;
3058  TrainController->BaseTime = TDateTime::CurrentDateTime();
3060  Screen->Cursor = TCursor(-2); // Arrow
3061  Utilities->CallLogPop(1536);
3062  }
3063  catch(const Exception &e) //non-error catch //modified at v2.13.0 to catch all errors
3064  {
3065  Screen->Cursor = TCursor(-2); // Arrow;
3066  UnicodeString MessageStr = "Unable to write the image to file, it may be too big";
3067  Application->MessageBox(MessageStr.c_str(), L"", MB_OK | MB_ICONWARNING);
3068  Utilities->CallLogPop(2298);
3069  }
3070 }
3071 // ---------------------------------------------------------------------------
3072 
3073 void __fastcall TInterface::SaveImageAndPrefDirsMenuItemClick(TObject *Sender)
3074 {
3075  // need to stop clock in case invoke during operation
3076  try
3077  {
3078  TrainController->LogEvent("SaveImageAndPrefDirsMenuItemClick");
3079  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveImageAndPrefDirsMenuItemClick");
3080  if(!DirectoryExists(CurDir + "\\" + IMAGE_DIR_NAME))
3081  {
3082  ShowMessage("Failed to find folder " + IMAGE_DIR_NAME + " in the folder where 'railway.exe' resides. Image can't be saved");
3083  Utilities->CallLogPop(1697);
3084  return;
3085  }
3086  Screen->Cursor = TCursor(-11); // Hourglass;
3087  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
3089  AnsiString ImageFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
3090  // format "16/06/2009 20:55:17"
3091  // avoid characters in filename:= / \ : * ? " < > |
3092  ImageFileName = CurDir + "\\" + IMAGE_DIR_NAME + "\\RailwayImage " + ImageFileName + "; " + RailwayTitle + ".bmp";
3093  AnsiString ShortName = "";
3094  for(int x = ImageFileName.Length(); x > 0; x--)
3095  {
3096  if(ImageFileName[x] == '\\')
3097  {
3098  ShortName = ImageFileName.SubString(x + 1, ImageFileName.Length() - x - 4);
3099  break;
3100  }
3101  }
3102  ShowMessage("A bitmap file named " + ShortName + " will be created in the Images folder");
3103  Graphics::TBitmap *RailwayImage = new Graphics::TBitmap;
3104  RailwayImage->PixelFormat = pf8bit; // needed to ensure compatibility with track
3105  int HPosMin = Track->GetHLocMin() * 16;
3106  int HPosMax = (Track->GetHLocMax() + 1) * 16;
3107  int VPosMin = Track->GetVLocMin() * 16;
3108  int VPosMax = (Track->GetVLocMax() + 1) * 16;
3109  RailwayImage->Width = HPosMax - HPosMin;
3110  RailwayImage->Height = VPosMax - VPosMin;
3111 
3112  // need to check if there is any text that extends past HPosMax or below VPosMax
3113  // use font height x text length x .7 for new rh calc & font height * 1.5 for new bottom calc
3114  if(!TextHandler->TextVector.empty())
3115  {
3116  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr != TextHandler->TextVector.end(); TextPtr++)
3117  {
3118  int NewWidth = (TextPtr->HPos - HPosMin) + (abs(TextPtr->Font->Height) * TextPtr->TextString.Length() * 0.7);
3119  int NewHeight = (TextPtr->VPos - VPosMin) + (abs(TextPtr->Font->Height) * 1.5);
3120  if(NewWidth > RailwayImage->Width)
3121  {
3122  RailwayImage->Width = NewWidth;
3123  }
3124  if(NewHeight > RailwayImage->Height)
3125  {
3126  RailwayImage->Height = NewHeight;
3127  }
3128  }
3129  }
3130  RailwayImage->Canvas->Brush->Color = clB5G5R5; // set it all to white initially
3131  TRect Rect(0, 0, RailwayImage->Width, RailwayImage->Height);
3132  RailwayImage->Canvas->FillRect(Rect);
3133 
3134  // write graphics first so text & track overwrite
3135  Track->WriteGraphicsToImage(2, RailwayImage);
3136  // then write track & text so text overwrites graphics & inactive elements
3137  Track->WriteTrackAndTextToImage(2, RailwayImage); //changed name & added text after inactives at v2.10.0
3138  EveryPrefDir->WritePrefDirToImage(0, RailwayImage);
3139  RailwayImage->SaveToFile(ImageFileName);
3140  delete RailwayImage;
3141  TrainController->BaseTime = TDateTime::CurrentDateTime();
3143  Screen->Cursor = TCursor(-2); // Arrow
3144  Utilities->CallLogPop(1566);
3145  }
3146  catch(const Exception &e) //non-error catch //modified at v2.13.0 to catch all errors
3147  {
3148  Screen->Cursor = TCursor(-2); // Arrow;
3149  UnicodeString MessageStr = "Unable to write the image to file, it may be too big";
3150  Application->MessageBox(MessageStr.c_str(), L"", MB_OK | MB_ICONWARNING);
3151  Utilities->CallLogPop(2299);
3152  }
3153 }
3154 // ---------------------------------------------------------------------------
3155 
3156 void __fastcall TInterface::SaveOperatingImageMenuItemClick(TObject *Sender)
3157 {
3158  // need to stop clock
3159  try
3160  {
3161  TrainController->LogEvent("SaveOperatingImageMenuItemClick");
3162  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveOperatingImageMenuItemClick");
3163  if(!DirectoryExists(CurDir + "\\" + IMAGE_DIR_NAME))
3164  {
3165  ShowMessage("Failed to find folder " + IMAGE_DIR_NAME + " in the folder where 'railway.exe' resides. Image can't be saved");
3166  Utilities->CallLogPop(1702);
3167  return;
3168  }
3169  Screen->Cursor = TCursor(-11); // Hourglass;
3170  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
3172 
3173  AnsiString TimetableTimeStr = Utilities->Format96HHMMSS(TrainController->TTClockTime);
3174  TimetableTimeStr = TimetableTimeStr.SubString(1, 2) + '.' + TimetableTimeStr.SubString(4, 2) + '.' + TimetableTimeStr.SubString(7, 2);
3175  AnsiString ImageFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
3176  // format "16/06/2009 20:55:17"
3177  // avoid characters in filename:= / \ : * ? " < > |
3178  ImageFileName = CurDir + "\\" + IMAGE_DIR_NAME + "\\RailwayImage " + ImageFileName + "; Timetable time " + TimetableTimeStr + "; " + RailwayTitle +
3179  "; " + TimetableTitle + ".bmp";
3180  AnsiString ShortName = "";
3181  for(int x = ImageFileName.Length(); x > 0; x--)
3182  {
3183  if(ImageFileName[x] == '\\')
3184  {
3185  ShortName = ImageFileName.SubString(x + 1, ImageFileName.Length() - x - 4);
3186  break;
3187  }
3188  }
3189  ShowMessage("A bitmap file named " + ShortName + " will be created in the Images folder");
3190  Graphics::TBitmap *RailwayImage = new Graphics::TBitmap;
3191  RailwayImage->PixelFormat = pf8bit; // needed to ensure compatibility with track
3192  int HPosMin = Track->GetHLocMin() * 16;
3193  int HPosMax = (Track->GetHLocMax() + 1) * 16;
3194  int VPosMin = Track->GetVLocMin() * 16;
3195  int VPosMax = (Track->GetVLocMax() + 1) * 16;
3196  RailwayImage->Width = HPosMax - HPosMin;
3197  RailwayImage->Height = VPosMax - VPosMin;
3198 
3199  // need to check if there is any text that extends past HPosMax or below VPosMax
3200  // use font height x text length x .7 for new rh calc & font height * 1.5 for new bottom calc
3201  if(!TextHandler->TextVector.empty())
3202  {
3203  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr != TextHandler->TextVector.end(); TextPtr++)
3204  {
3205  int NewWidth = (TextPtr->HPos - HPosMin) + (abs(TextPtr->Font->Height) * TextPtr->TextString.Length() * 0.7);
3206  int NewHeight = (TextPtr->VPos - VPosMin) + (abs(TextPtr->Font->Height) * 1.5);
3207  if(NewWidth > RailwayImage->Width)
3208  {
3209  RailwayImage->Width = NewWidth;
3210  }
3211  if(NewHeight > RailwayImage->Height)
3212  {
3213  RailwayImage->Height = NewHeight;
3214  }
3215  }
3216  }
3217  RailwayImage->Canvas->Brush->Color = clB5G5R5; // set it all to white initially
3218  TRect Rect(0, 0, RailwayImage->Width, RailwayImage->Height);
3219  RailwayImage->Canvas->FillRect(Rect);
3220 
3221  // write graphics first so text & track overwrite
3222  Track->WriteGraphicsToImage(3, RailwayImage);
3223  // then write track and text so both overwrite graphics & text overwrites inactive elements
3224  Track->WriteOperatingTrackAndTextToImage(0, RailwayImage); // need points with single fillets, signals with colours, gaps all connected
3225  AllRoutes->WriteAllRoutesToImage(0, RailwayImage);
3226 // add any locked route markers
3227  if(!AllRoutes->LockedRouteVector.empty())
3228  {
3229  for(TAllRoutes::TLockedRouteVectorIterator LRVIT = AllRoutes->LockedRouteVector.end() - 1; LRVIT >= AllRoutes->LockedRouteVector.begin(); LRVIT--)
3230  {
3231  TOneRoute Route = AllRoutes->GetFixedRouteAt(167, LRVIT->RouteNumber);
3232  int x = Route.PrefDirSize() - 1;
3233  bool BreakFlag = false;
3234  TPrefDirElement PrefDirElement = Route.GetFixedPrefDirElementAt(188, x);
3235  while(PrefDirElement.GetTrackVectorPosition() != LRVIT->RearTrackVectorPosition)
3236  {
3237  RailwayImage->Canvas->Draw((PrefDirElement.HLoc - Track->GetHLocMin()) * 16, (PrefDirElement.VLoc - Track->GetVLocMin()) * 16,
3238  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
3239  if(!(AllRoutes->TrackIsInARoute(13, PrefDirElement.Conn[PrefDirElement.GetELinkPos()],
3240  PrefDirElement.ConnLinkPos[PrefDirElement.GetELinkPos()])))
3241  {
3242  BreakFlag = true;
3243  break; // train removed earlier element from route so stop here
3244  }
3245  x--;
3246  PrefDirElement = Route.GetFixedPrefDirElementAt(180, x);
3247  }
3248  if(!BreakFlag)
3249  {
3250  if(PrefDirElement.GetTrackVectorPosition() == LRVIT->RearTrackVectorPosition)
3251  {
3252  RailwayImage->Canvas->Draw((PrefDirElement.HLoc - Track->GetHLocMin()) * 16, (PrefDirElement.VLoc - Track->GetVLocMin()) * 16,
3253  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
3254  }
3255  }
3256  }
3257  }
3258  TrainController->WriteTrainsToImage(0, RailwayImage);
3259  RailwayImage->SaveToFile(ImageFileName);
3260  delete RailwayImage;
3261  TrainController->BaseTime = TDateTime::CurrentDateTime();
3263  Screen->Cursor = TCursor(-2); // Arrow
3264  Utilities->CallLogPop(1703);
3265  }
3266  catch(const Exception &e) //non-error catch //modified at v2.13.0 to catch all errors
3267  {
3268  Screen->Cursor = TCursor(-2); // Arrow;
3269  UnicodeString MessageStr = "Unable to write the image to file, it may be too big";
3270  Application->MessageBox(MessageStr.c_str(), L"", MB_OK | MB_ICONWARNING);
3271  Utilities->CallLogPop(2540);
3272  }
3273 }
3274 
3275 // ---------------------------------------------------------------------------
3276 
3277 void __fastcall TInterface::SaveHeaderMenu1Click(TObject *Sender)
3278 {
3279 //
3280  try
3281  {
3282  TrainController->LogEvent("SaveHeaderMenu1Click");
3283  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveHeaderMenu1Click");
3284  ExitHeatmaps();
3285  if(Sender == SaveSessionButton)
3286  {
3287  SaveSessionFlag = true;
3288  }
3289  else if(SavedFileName == "") // use 'Save As' function
3290  {
3291  SaveAsSubroutine(1);
3292  }
3293  else // ordinary save
3294  {
3295  Screen->Cursor = TCursor(-11); // Hourglass;
3296  std::ofstream VecFile(SavedFileName.c_str());
3297  if(!(VecFile.fail()))
3298  {
3302  // save track elements
3303  if(Track->UserGraphicVector.empty())
3304  {
3305  Track->SaveTrack(9, VecFile, false); // false for no graphics (**Active elements** saved as marker)
3306  }
3307  else
3308  {
3309  Track->SaveTrack(10, VecFile, true); // true for graphics to be saved (**Active elements**1 saved as marker)
3310  }
3311  // save text elements
3312  TextHandler->SaveText(5, VecFile);
3313  // save PrefDir elements
3314  EveryPrefDir->SavePrefDirVector(8, VecFile);
3315  if(!Track->UserGraphicVector.empty())
3316  {
3317  // save user graphics
3318  Track->SaveUserGraphics(1, VecFile);
3319  }
3320  FileChangedFlag = false;
3321  VecFile.close();
3322  }
3323  else
3324  {
3325  ShowMessage("Railway failed to save - can't open file");
3326  }
3327  Screen->Cursor = TCursor(-2); // Arrow
3328  }
3329  Utilities->CallLogPop(1552);
3330  }
3331  catch(const Exception &e) //non-error catch added at 2.14.0
3332  {
3333  TrainController->StopTTClockMessage(135, "Save failed\nError message: " + e.Message);
3334  Screen->Cursor = TCursor(-2); // Arrow;
3335 // ErrorLog(44, e.Message);
3336  }
3337 }
3338 
3339 // ---------------------------------------------------------------------------
3340 void __fastcall TInterface::LoadSessionMenuItemClick(TObject *Sender)
3341 {
3342  try
3343  {
3344  TrainController->LogEvent("LoadSessionMenuItemClick");
3345  LoadSessionFlag = true; // load session within ClockTimer2
3346  }
3347  catch(const Exception &e)
3348  {
3349  ErrorLog(136, e.Message);
3350  }
3351 }
3352 
3353 // ---------------------------------------------------------------------------
3354 void __fastcall TInterface::ClearAllMenuItemClick(TObject *Sender)
3355 {
3356  try
3357  {
3358  TrainController->LogEvent("ClearAllMenuItemClick");
3359  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ClearAllMenuItemClick");
3360  if(ClearEverything(2))
3361  {
3362  ;
3363  } // no change in action on result
3364 
3365  Level1Mode = BaseMode;
3366  SetLevel1Mode(126);
3367  Utilities->CallLogPop(1179);
3368  }
3369  catch(const Exception &e)
3370  {
3371  ErrorLog(137, e.Message);
3372  }
3373 }
3374 
3375 // ---------------------------------------------------------------------------
3376 void __fastcall TInterface::ExportTTMenuItemClick(TObject *Sender)
3377 {
3378  // no need to stop clock as can't be called when railway operating
3379  try
3380  {
3381  TrainController->LogEvent("ExportTTMenuItemClick");
3382  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExportTTMenuItemClick");
3383  if(!DirectoryExists(CurDir + "\\" + FORMATTEDTT_DIR_NAME))
3384  {
3385  ShowMessage("Failed to find folder " + FORMATTEDTT_DIR_NAME + " in the folder where 'railway.exe' resides. Timetable can't be exported");
3386  Utilities->CallLogPop(1699);
3387  return;
3388  }
3389 // Screen->Cursor = TCursor(-11); // Hourglass; - no good setting here as it's reset by ShowMessage in CreateFormattedTimetable. It's set there after
3390 // the message instead, but reset here afterwards
3391  // no need to stop clock as can't select this if operating
3393  Screen->Cursor = TCursor(-2); // Arrow - reset after above function returns
3394  Utilities->CallLogPop(1573);
3395  }
3396  catch(const Exception &e)
3397  {
3398  ErrorLog(138, e.Message);
3399  }
3400 }
3401 // ---------------------------------------------------------------------------
3402 // Timetable editing functions
3403 
3404 /* Note that during early development the timetable was created outside the program as a .csv file using Excel, it was only later that
3405  the editing functions within the program were developed. Much of the original structure was preserved though to avoid rewriting the
3406  code interpretation functions in TrainUnit.cpp. This is why commas are used as service event separators, and why it is necessary to
3407  convert them to CRLFs for display and back again for internal storage. It is acknowledged that all this makes the editing functions
3408  somewhat cumbersome, and, as ever, if I was starting again I wouldn't do it like that!
3409 
3410  CR & LF review:
3411  These cause problems by the way that different subroutines handle them.
3412 
3413  AnsiStrings can incorporate CRLFs, but the end of an AnsiString is marked by a '\0' character as in 'C' strings.
3414 
3415  In the fstream functions 'getline(char_type* s, streamsize n)' extracts characters from the stream and puts them in buffer 's' until
3416  (a) n-1 characters are stored + '\0' after the n-1 characters;
3417  (b) a '\n' (CRLF) character is found in the stream, in which case a '\0' is added to the buffer after the text that immediately
3418  precedes the CRLF in the stream; and
3419  (c) an eof() is found in the stream, in which case a '\0' is added to the buffer at the end of the text.
3420  Note that if no characters are stored a '\0' is still stored in position [0] of the buffer.
3421 
3422  The << operator in ofstreams, when used with a null terminated string, doesn't store the null. If it is required it has to be
3423  sent explicitly, e.g. file << '\0'. Presumably the same applies for CRLF terminated strings.
3424 
3425 */
3426 // ---------------------------------------------------------------------------
3427 
3428 void __fastcall TInterface::CreateTimetableMenuItemClick(TObject *Sender)
3429 {
3430  try
3431  {
3432  TrainController->LogEvent("CreateTimetableMenuItemClick");
3433  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CreateTimetableMenuItemClick");
3434  CreateEditTTFileName = "";
3435  TimetableEditVector.clear();
3436  TimetableEditPanel->Visible = true;
3437  TrainController->TTEditPanelVisible = true; // added at v2.6.0 for two location message
3438  HighlightPanel->Visible = false;
3439  TimetablePanel->Visible = true;
3440  TimetablePanel->BringToFront(); // in case SaveRailway button visible, want it hidden else obscures the panel text
3441  ShowHideTTButton->Glyph->LoadFromResourceName(0, "Hide");
3442  OneEntryTimetableMemo->Clear();
3443  AllEntriesTTListBox->Clear();
3444  TTStartTimeBox->Text = "";
3445  AddSubMinsBox->Text = "";
3447  LocationNameComboBox->Clear();
3448  TimetableTitle = ""; // unload any loaded timetable. Added here at v2.1.0
3449  TrainController->TrainDataVector.clear(); // unload any loaded timetable. Added here at v2.1.0
3450  SetCaption(9); // added at v2.1.0 as formerly retained earlier loaded tt name in error
3451  TimetableChangedFlag = false;
3452  TimetableValidFlag = false;
3453  TTEntryChangedFlag = false;
3456  AZOrderButton->Caption = AnsiString("A-Z Order");
3457  AZOrderButton->Hint = AnsiString("Arrange services in alphabetical order Toggle with Shift+ Z");
3458  TimeOrderButton->Caption = AnsiString("Time Order");
3459  TimeOrderButton->Hint = AnsiString("Arrange services in chronological (time) order Toggle with Shift+ Y");
3460  CopiedEntryFlag = false;
3461  NewEntryInPreparationFlag = false;
3462  CopiedEntryStr = "";
3463  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
3468 
3469 // populate LocationNameComboBox if a railway is loaded, but first compile the ActiveTrackElementNameMap
3470  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
3472  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
3473  {
3475  == Track->ContinuationNameMap.end())
3476  {
3477  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
3478  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1038, x).ActiveTrackElementName;
3479  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
3480  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
3481  }
3482  }
3484  if(!(Track->ActiveTrackElementNameMap.empty()))
3485  {
3486  LocationNameComboBox->Text = "Location names";
3487 // new version at beta v0.2b
3489  ATENIT++)
3490  {
3491  LocationNameComboBox->Items->Add(ATENIT->first); // continuations excluded during compilation, but a location that includes
3492  // continuations as well as other track will be included - earlier version
3493  // would have excluded them
3494  }
3495 
3496 /* old version using LocationNameMultiMap, changed to use ActiveTrackElementNames to avoid including lone concourses and named non-station
3497  locations
3498  TStringList *StringList = new TStringList;
3499  StringList->Clear();//probably already empty but help file doesn't say so
3500  StringList->Sorted = false;//for now
3501  for(TTrack::TLocationNameMultiMapIterator LNMIT = Track->LocationNameMultiMap.begin(); LNMIT != Track->LocationNameMultiMap.end(); LNMIT++)
3502  {
3503  NewKey = LNMIT->first;
3504  if(OldKey != NewKey)//only add new values
3505  {
3506  if(Track->ContinuationNameMap.find(NewKey) == Track->ContinuationNameMap.end())//not a continuation
3507  {
3508  StringList->Add(NewKey);
3509  OldKey = NewKey;
3510  }
3511  }
3512  }
3513  StringList->Sort();
3514  for(int x=0;x<StringList->Count;x++)
3515  {
3516  LocationNameComboBox->Items->Add(StringList->Strings[x]);
3517  }
3518  delete StringList;
3519 */
3520  }
3521  else
3522  {
3523  LocationNameComboBox->Text = "No locations (listed when a railway with names is loaded)";
3524  }
3526  SetLevel1Mode(82);
3527  session_api_->dump(); // update session INI file //added at v2.10.0
3528  Utilities->CallLogPop(1595);
3529  }
3530  catch(const Exception &e)
3531  {
3532  ErrorLog(47, e.Message);
3533  }
3534 }
3535 
3536 // ---------------------------------------------------------------------------
3537 void __fastcall TInterface::EditTimetableMenuItemClick(TObject *Sender)
3538 /* The .ttb file contains a sequence of AnsiStrings separated by null characters. CRLFs may be embedded within the AnsiStrings,
3539  * to cause newlines when displayed. Each AnsiString corresponds to a timetable 'entry'
3540 */
3541 {
3542  try
3543  {
3544  TrainController->LogEvent("EditTimetableMenuItemClick");
3545  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",EditTimetableMenuItemClick");
3546  SigImagePanel->Visible = false; // stop panel showing while waiting for name entry
3547  TimetableDialog->Filter = "Timetable file (*.ttb)|*ttb";
3548  CreateEditTTFileName = "";
3549  TimetableEditVector.clear();
3550  TimetableEditPanel->Visible = true;
3551  TrainController->TTEditPanelVisible = true; // added at v2.6.0 for two location message
3552  HighlightPanel->Visible = false;
3553  TimetablePanel->Visible = true;
3554  TimetablePanel->BringToFront(); // in case SaveRailway button visible, want it hidden else obscures the panel text
3555  ShowHideTTButton->Glyph->LoadFromResourceName(0, "Hide");
3556  OneEntryTimetableMemo->Clear();
3557  AllEntriesTTListBox->Clear();
3558  TTStartTimeBox->Text = "";
3559  AddSubMinsBox->Text = "";
3561  LocationNameComboBox->Clear();
3562  TimetableTitle = ""; // unload any loaded timetable. Moved here from below at v2.1.0 for consistency with CreateTimetable
3563  TrainController->TrainDataVector.clear(); // unload any loaded timetable. Moved here from below at v2.1.0 for consistency with CreateTimetable
3564  SetCaption(8); // added at v2.1.0 as formerly retained earlier loaded tt name in error
3565  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
3570  if(TimetableDialog->Execute())
3571  {
3572  if(TimetableDialog->InitialDir != TPath::GetDirectoryName(TimetableDialog->FileName)) // new at v2.6.0 to retain a new directory
3573  {
3574  TimetableDialog->InitialDir = TPath::GetDirectoryName(TimetableDialog->FileName);
3575  SaveTTDialog->InitialDir = TPath::GetDirectoryName(TimetableDialog->FileName);
3576  }
3577  CreateEditTTFileName = AnsiString(TimetableDialog->FileName);
3578  TrainController->LogEvent("EditTimetable " + CreateEditTTFileName);
3579  std::ifstream TTBLFile(CreateEditTTFileName.c_str(), std::ios_base::binary); // open in binary to examine each character
3580  if(TTBLFile.is_open())
3581  {
3582  // check doesn't contain any control characters except CR, LF & '\0' (changed at v2.16.0 to allow extended characters in location names)
3583  char c;
3584  while(!TTBLFile.eof())
3585  {
3586  TTBLFile.get(c);
3587  if((c > 0) && (c < 32) && (c != 13) && (c != 10)) //added (c > 0) & dropped (c != 0) at v2.17.0 as extended characters treated as negative
3588 // if((c < 32) && (c >= 1)) //have to allow NULLs - dropped at v2.16.1 as prevented CRLF which is ok so used condition above
3589  {
3590  ShowMessage("Timetable file contains invalid control characters");
3591  TTBLFile.close();
3592  Utilities->CallLogPop(1612);
3593  return;
3594  }
3595  }
3596  session_api_->write_string("timetable_file", TimetableDialog->FileName); // API v1.2
3597  TTBLFile.close();
3598  }
3599  else
3600  {
3601  ShowMessage("Failed to open timetable file " + CreateEditTTFileName + ", make sure it's spelled correctly, it exists and isn't open in another application");
3602  Utilities->CallLogPop(1597);
3603  return;
3604  }
3605  // reopen again in binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
3606  Delay(4, 100); // 100mSec delay between closing & re-opening file
3607  TTBLFile.open(CreateEditTTFileName.c_str(), std::ios_base::binary);
3608  if(TTBLFile.is_open())
3609  {
3610  TTBLFile.clear(); // to clear eofbit from last read
3611  TTBLFile.seekg(0); // shouldn't be needed but include for safety
3612  TimetableChangedFlag = false;
3613  TimetableValidFlag = false;
3614  TTEntryChangedFlag = false;
3617  AZOrderButton->Caption = AnsiString("A-Z Order");
3618  AZOrderButton->Hint = AnsiString("Arrange services in alphabetical order Toggle with Shift+ Z");
3619  TimeOrderButton->Caption = AnsiString("Time Order");
3620  TimeOrderButton->Hint = AnsiString("Arrange services in chronological (time) order Toggle with Shift+ Y");
3621  NewEntryInPreparationFlag = false;
3622  CopiedEntryStr = "";
3623  CopiedEntryFlag = false;
3624 // CreateEditTTFileName = TimetableDialog->FileName;
3625  for(int x = CreateEditTTFileName.Length(); x > 0; x--)
3626  {
3627  if(CreateEditTTFileName[x] == '\\')
3628  {
3629  CreateEditTTTitle = CreateEditTTFileName.SubString(x + 1, CreateEditTTFileName.Length() - x - 4);
3630  break;
3631  }
3632  }
3633  char *TimetableEntryString = new char[10000];
3634  while(true)
3635  {
3636  TTBLFile.getline(TimetableEntryString, 10000, '\0'); // pick up the entire AnsiString, including any embedded newlines
3637  if(strlen(TimetableEntryString) > 9997) //added at v2.18.0 because of Jason B's error reading a file without NULs (07/12/23)
3638  { //can't use failbit as the compiler says it's always true - don't understand why
3639  ShowMessage("Unable to read a line from file " + CreateEditTTFileName + ".\nEither the file is corrupt or a single service entry exceeds 10,000 characters.");
3640  Utilities->CallLogPop(2689);
3641  return;
3642  }
3643  if(TTBLFile.eof() && (TimetableEntryString[0] == '\0')) // stores a null in 1st position if doesn't load any characters
3644  {
3645  // may still have eof even if read a line, and
3646  // if so need to process it
3647  break;
3648  }
3649  AnsiString OneLine(TimetableEntryString);
3650  TimetableEditVector.push_back(OneLine);
3651  }
3652  TTBLFile.close();
3653  delete[]TimetableEntryString;
3654  // here with TimetableEditVector compiled
3655  }
3656  else
3657  {
3658  ShowMessage("Failed to open timetable file " + CreateEditTTFileName + ", make sure it's spelled correctly, it exists and isn't open in another application");
3659  Utilities->CallLogPop(1654);
3660  return;
3661  }
3662  }
3663  else // cancelled dialog [section prior to CallLogPop added for v1.3.2 to clear timetable screen if cancel button pressed]
3664  {
3665  CreateEditTTFileName = "";
3666 // set to null to allow a check during error file saving, if not null save the tt being edited to the file (see entry in ExitTTModeButtonClick)
3667  CreateEditTTTitle = ""; // as above
3668  Level1Mode = BaseMode;
3669  SetLevel1Mode(132);
3670  Utilities->CallLogPop(1633);
3671  return;
3672  }
3674  if(TimetableEditVector.empty())
3675  {
3677  SetLevel1Mode(89);
3678  Utilities->CallLogPop(1614);
3679  return;
3680  }
3681 // all now set where can be
3683 
3684 // populate LocationNameComboBox if a railway is loaded, but first compile the ActiveTrackElementNameMap
3685  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
3687  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
3688  {
3690  == Track->ContinuationNameMap.end())
3691  {
3692  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
3693  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1041, x).ActiveTrackElementName;
3694  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
3695  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
3696  }
3697  }
3699  if(!(Track->ActiveTrackElementNameMap.empty()))
3700  {
3701  LocationNameComboBox->Text = "Location names";
3702 // new version for beta v0.2b
3704  ATENIT++)
3705  {
3706  LocationNameComboBox->Items->Add(ATENIT->first); // continuations excluded during compilation, but a location that includes
3707  // continuations as well as other track will be included - earlier version
3708  // would have excluded them
3709  }
3710  }
3711  else
3712  {
3713  LocationNameComboBox->Text = "No locations (listed when a railway with names is loaded)";
3714  }
3716  SetLevel1Mode(83);
3717  session_api_->dump(); // update session INI file //added at v2.10.0
3718  Utilities->CallLogPop(1596);
3719  }
3720  catch(const Exception &e) //non-error catch added at v2.14.0
3721  {
3722  TrainController->StopTTClockMessage(139, "Timetable file failed to load - is the latest program version in use?\nIf so then the file may be corrupt.\n\nError message: " + e.Message);
3723  Screen->Cursor = TCursor(-2); // Arrow;
3724  Utilities->CallLogPop(2555);
3725 // ErrorLog(48, e.Message);
3726  }
3727 }
3728 // ---------------------------------------------------------------------------
3729 
3730 void __fastcall TInterface::ShowHideTTButtonClick(TObject *Sender)
3731 {
3732  try
3733  {
3734  TrainController->LogEvent("ShowHideTTButtonClick");
3735  if(TimetableEditPanel->Visible)
3736  {
3737  ShowHideTTButton->Glyph->LoadFromResourceName(0, "Show");
3738  TimetableEditPanel->Visible = false;
3739  TrainController->TTEditPanelVisible = false; // added at v2.6.0 for two location message
3740  ShowHideTTButton->Hint = "Show the timetable editor Shift S";
3741 // InfoPanel->Visible = false; //changed at v1.3.0 to make it clearer that still in TT mode
3742  InfoPanel->Caption = "Timetable mode: editor hidden"; // as above
3743  }
3744  else
3745  {
3746  ShowHideTTButton->Glyph->LoadFromResourceName(0, "Hide");
3747  TimetableEditPanel->Visible = true;
3748  TrainController->TTEditPanelVisible = true; // added at v2.6.0 for two location message
3749  ShowHideTTButton->Hint = "Hide the timetable editor to see the railway Shift H";
3751  SetLevel1Mode(124);
3752  }
3753  }
3754  catch(const Exception &e)
3755  {
3756  ErrorLog(139, e.Message);
3757  }
3758 }
3759 // ---------------------------------------------------------------------------
3760 
3761 void __fastcall TInterface::NextTTEntryButtonClick(TObject *Sender)
3762 {
3763  try
3764  {
3765  TrainController->LogEvent("NextTTEntryButtonClick");
3766  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",NextTTEntryButtonClick");
3768  {
3769  Utilities->CallLogPop(1683);
3770  return;
3771  }
3773  {
3775  }
3776  TTEntryChangedFlag = false;
3777  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
3778  // position changing in AllEntriesTTListBox
3780  SetLevel1Mode(85);
3781  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
3782  {
3784  }
3785  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
3786  {
3788  }
3789  else
3790  {
3791  AllEntriesTTListBox->TopIndex = TopPos;
3792  }
3793  Utilities->CallLogPop(1605);
3794  }
3795  catch(const Exception &e)
3796  {
3797  ErrorLog(50, e.Message);
3798  }
3799 }
3800 
3801 // ---------------------------------------------------------------------------
3802 void __fastcall TInterface::PreviousTTEntryButtonClick(TObject *Sender)
3803 {
3804  try
3805  {
3806  TrainController->LogEvent("PreviousTTEntryButtonClick");
3807  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PreviousTTEntryButtonClick");
3809  {
3810  Utilities->CallLogPop(1684);
3811  return;
3812  }
3814  {
3816  }
3817  TTEntryChangedFlag = false;
3818  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
3819  // position changing in AllEntriesTTListBox
3821  SetLevel1Mode(86);
3822  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
3823  {
3825  }
3826  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
3827  {
3829  }
3830  else
3831  {
3832  AllEntriesTTListBox->TopIndex = TopPos;
3833  }
3834  Utilities->CallLogPop(1607);
3835  }
3836  catch(const Exception &e)
3837  {
3838  ErrorLog(51, e.Message);
3839  }
3840 }
3841 
3842 // ---------------------------------------------------------------------------
3843 void __fastcall TInterface::NewTTEntryButtonClick(TObject *Sender)
3844 {
3845  try
3846  {
3847  TrainController->LogEvent("NewTTEntryButtonClick");
3848  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",NewTTEntryButtonClick");
3849  OneEntryTimetableMemo->Clear();
3850  OneEntryTimetableMemo->SetFocus();
3853  SetLevel1Mode(103);
3854  Utilities->CallLogPop(1615);
3855  }
3856  catch(const Exception &e)
3857  {
3858  ErrorLog(52, e.Message);
3859  }
3860 }
3861 // ---------------------------------------------------------------------------
3862 
3863 void __fastcall TInterface::AddMinsButtonClick(TObject *Sender)
3864 {
3865  try
3866  {
3867  TrainController->LogEvent("AddMinsButtonClick");
3868  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddMinsButtonClick");
3869  bool ValidFlag = true;
3870  for(int x = 1; x <= AddSubMinsBox->Text.Length(); x++)
3871  {
3872  if((AddSubMinsBox->Text[x] > '9') || (AddSubMinsBox->Text[x] < '0')) // tested in TTHandler but check here as a safeguard
3873  {
3874  ValidFlag = false;
3875  break;
3876  }
3877  }
3878  if(ValidFlag)
3879  {
3880  if(AddSubMinsBox->Text.ToInt() == 0)
3881  {
3882  ValidFlag = false;
3883  }
3884  }
3885  if((TTCurrentEntryIterator == TimetableEditVector.end()) || (*TTCurrentEntryIterator == "") || (AddSubMinsBox->Text == "") || !ValidFlag)
3886  {
3887  Utilities->CallLogPop(1649);
3888  return;
3889  }
3890  TDateTime DummyTime;
3891  int AddMins = AddSubMinsBox->Text.ToInt();
3892  for(int x = 0; x < OneEntryTimetableMemo->Lines->Count; x++)
3893  {
3894  for(int y = 1; y < (OneEntryTimetableMemo->Lines->Strings[x].Length() - 3); y++)
3895  {
3896  if(TrainController->CheckTimeValidity(25, OneEntryTimetableMemo->Lines->Strings[x].SubString(y, 5), DummyTime))
3897  {
3898  int Mins = OneEntryTimetableMemo->Lines->Strings[x].SubString(y + 3, 2).ToInt();
3899  int Hrs = OneEntryTimetableMemo->Lines->Strings[x].SubString(y, 2).ToInt();
3900  Mins += AddMins;
3901  while(Mins >= 60)
3902  {
3903  Mins -= 60;
3904  Hrs++;
3905  }
3906  if(Hrs > 95)
3907  {
3908  ShowMessage("One or more times excessive, not permitted to exceed 95 hours");
3909  Utilities->CallLogPop(1650);
3910  return;
3911  }
3912  AnsiString MinsStr = AnsiString(Mins), HrsStr = AnsiString(Hrs);
3913  if(Mins < 10)
3914  {
3915  MinsStr = "0" + MinsStr;
3916  }
3917  if(Hrs < 10)
3918  {
3919  HrsStr = "0" + HrsStr;
3920  }
3921  int StrLength = OneEntryTimetableMemo->Lines->Strings[x].Length();
3922  AnsiString NewString = OneEntryTimetableMemo->Lines->Strings[x].SubString(1, (y - 1)); // up to but not including the time
3923  NewString += HrsStr + ':' + MinsStr;
3924  NewString += OneEntryTimetableMemo->Lines->Strings[x].SubString((y + 5), (StrLength - y - 4));
3925  OneEntryTimetableMemo->Lines->Strings[x] = NewString;
3926  }
3927  }
3928  }
3929 
3930  OneEntryTimetableMemo->HideSelection = true;
3931  OneEntryTimetableMemo->SelStart = 0; // need this & next command to set cursor to the top
3932  OneEntryTimetableMemo->SelLength = 0;
3933  TimetableValidFlag = false;
3934  TimetableChangedFlag = true;
3935  TTEntryChangedFlag = true;
3937  SetLevel1Mode(91);
3938  Utilities->CallLogPop(1617);
3939  }
3940  catch(const Exception &e)
3941  {
3942  ErrorLog(54, e.Message);
3943  }
3944 }
3945 // ---------------------------------------------------------------------------
3946 
3947 void __fastcall TInterface::SubMinsButtonClick(TObject *Sender)
3948 {
3949  try
3950  {
3951  TrainController->LogEvent("SubMinsButtonClick");
3952  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SubMinsButtonClick");
3953  bool ValidFlag = true;
3954  for(int x = 1; x <= AddSubMinsBox->Text.Length(); x++)
3955  {
3956  if((AddSubMinsBox->Text[x] > '9') || (AddSubMinsBox->Text[x] < '0')) // tested in TTHandler but check here as a safeguard
3957  {
3958  ValidFlag = false;
3959  break;
3960  }
3961  }
3962  if(ValidFlag)
3963  {
3964  if(AddSubMinsBox->Text.ToInt() == 0)
3965  {
3966  ValidFlag = false;
3967  }
3968  }
3969  if((TTCurrentEntryIterator == TimetableEditVector.end()) || (*TTCurrentEntryIterator == "") || (AddSubMinsBox->Text == "") || !ValidFlag)
3970  {
3971  Utilities->CallLogPop(1659);
3972  return;
3973  }
3974  TDateTime DummyTime;
3975  int SubMins = AddSubMinsBox->Text.ToInt();
3976  for(int x = 0; x < OneEntryTimetableMemo->Lines->Count; x++)
3977  {
3978  for(int y = 1; y < (OneEntryTimetableMemo->Lines->Strings[x].Length() - 3); y++)
3979  {
3980  if(TrainController->CheckTimeValidity(28, OneEntryTimetableMemo->Lines->Strings[x].SubString(y, 5), DummyTime))
3981  {
3982  int Mins = OneEntryTimetableMemo->Lines->Strings[x].SubString(y + 3, 2).ToInt();
3983  int Hrs = OneEntryTimetableMemo->Lines->Strings[x].SubString(y, 2).ToInt();
3984  Mins -= SubMins;
3985  while(Mins < 0)
3986  {
3987  Mins += 60;
3988  Hrs--;
3989  }
3990  if(Hrs < 0)
3991  {
3992  ShowMessage("One or more times are now before 00:00, this is not permitted");
3993  Utilities->CallLogPop(1660);
3994  return;
3995  }
3996  AnsiString MinsStr = AnsiString(Mins), HrsStr = AnsiString(Hrs);
3997  if(Mins < 10)
3998  {
3999  MinsStr = "0" + MinsStr;
4000  }
4001  if(Hrs < 10)
4002  {
4003  HrsStr = "0" + HrsStr;
4004  }
4005  int StrLength = OneEntryTimetableMemo->Lines->Strings[x].Length();
4006  AnsiString NewString = OneEntryTimetableMemo->Lines->Strings[x].SubString(1, (y - 1)); // up to but not including the time
4007  NewString += HrsStr + ':' + MinsStr;
4008  NewString += OneEntryTimetableMemo->Lines->Strings[x].SubString((y + 5), (StrLength - y - 4));
4009  OneEntryTimetableMemo->Lines->Strings[x] = NewString;
4010  }
4011  }
4012  }
4013  OneEntryTimetableMemo->HideSelection = true;
4014  OneEntryTimetableMemo->SelStart = 0; // need this & next command to set cursor to the top
4015  OneEntryTimetableMemo->SelLength = 0;
4016  TimetableValidFlag = false;
4017  TimetableChangedFlag = true;
4018  TTEntryChangedFlag = true;
4020  SetLevel1Mode(92);
4021  Utilities->CallLogPop(1618);
4022  }
4023  catch(const Exception &e)
4024  {
4025  ErrorLog(55, e.Message);
4026  }
4027 }
4028 // ---------------------------------------------------------------------------
4029 
4030 void __fastcall TInterface::CopyTTEntryButtonClick(TObject *Sender)
4031 {
4032  try
4033  {
4034  TrainController->LogEvent("CopyTTEntryButtonClick");
4035  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CopyTTEntryButtonClick");
4037  {
4038  Utilities->CallLogPop(1636);
4039  return;
4040  }
4042  CopiedEntryFlag = true;
4043  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4044  // position changing in AllEntriesTTListBox
4046  SetLevel1Mode(93);
4047  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
4048  {
4050  }
4051  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
4052  {
4054  }
4055  else
4056  {
4057  AllEntriesTTListBox->TopIndex = TopPos;
4058  }
4059  Utilities->CallLogPop(1619);
4060  }
4061  catch(const Exception &e)
4062  {
4063  ErrorLog(56, e.Message);
4064  }
4065 }
4066 // ---------------------------------------------------------------------------
4067 
4068 void __fastcall TInterface::InvertTTEntryButtonClick(TObject *Sender) //added at v2.15.0
4069 {
4070  try
4071  {
4072  TrainController->LogEvent("InvertTTEntryButtonClick");
4073  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",InvertTTEntryButtonClick");
4074  TTimetableEditVector InputVector, OutputVector, OutputVector2, TimeVector1, TimeVector2; //TTimetableEditVector is an ansistring vector type so convenient to use
4075  typedef std::vector<int> TMinVector;
4076  TMinVector MinVector;
4077  AnsiString OneLine = *TTCurrentEntryIterator, SubStr, First, Second, Third, Fourth, TrainDataLine, NewEntry;
4078  AnsiString ArrivalTime, DepartureTime, FirstTimeLoc, FirstLocation;
4079  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
4080  TTimetableFormatType FormatType;
4081  TTimetableLocationType LocationType;
4082  TTimetableSequenceType SequenceType;
4083  TTimetableShuttleLinkType ShuttleLinkType;
4084  TNumList ExitList;
4085  bool FirstTimeLocRegistered = false;
4086  bool Warning, RepeatFlag = false;
4087  int Count = 1; // anything > 0 OK as if 0 still seeking a start time in ProcessOne...
4088  bool EndOfFile = false;
4089  bool FinalCallFalse = false;
4090  bool GiveMessagesFalse = false;
4091  bool CheckLocationsExistInRailwayFalse = false;
4093  if(!TrainController->ProcessOneTimetableLine(7, Count, *TTCurrentEntryIterator, EndOfFile, FinalCallFalse, GiveMessagesFalse, CheckLocationsExistInRailwayFalse))
4094  // return true for success
4095  {
4096  ShowMessage("There seem to be one or more errors in the syntax for the selected entry.\n\nPlease correct before inverting. ");
4097  Utilities->CallLogPop(2560);
4098  return;
4099  }
4100  else //Syntax ok
4101  { //parse the entry (*TTCurrentEntryIterator) storing all the valid lines in an ansistring vector
4102  while(true) //button only enabled if a legitimate service is highlighted
4103  {
4104  int Pos = OneLine.Pos(',');
4105  if(Pos == 0)//no more commas found so remainder is the last, unless there is no remainder
4106  {
4107  if(OneLine.Length() > 0)
4108  {
4109  InputVector.push_back(OneLine);
4110  }
4111  break;
4112  }
4113  else
4114  {
4115  SubStr = OneLine.SubString(1, Pos - 1);
4116  InputVector.push_back(SubStr);
4117  if(Pos == OneLine.Length()) //reached end and ends with a comma, probably not allowed but keep just in case
4118  {
4119  break;
4120  }
4121  OneLine = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
4122  continue;
4123  }
4124  }
4125  //here with InputVector populated, weed out any invalid entries
4126 /*
4127 NewEntry = ""; //populated (diagnostics)
4128 for(TTEVIterator IPVIt = InputVector.begin(); IPVIt < InputVector.end(); IPVIt++)
4129 {
4130  NewEntry += *IPVIt + ',';
4131 }
4132 CopiedEntryStr = NewEntry; //this is needed for pasting as a new entry
4133 PasteTTEntryButton->Click(); //paste it after the current entry
4134 */
4135 
4136  TDateTime TimeVal;
4137  if(InputVector.size() > 0)
4138  {
4139  for(TTEVIterator IPVIt = InputVector.end() - 1; IPVIt >= InputVector.begin(); IPVIt--) //backwards so can erase invalid entries
4140  {
4141  if(((*IPVIt)[1] == 'R') && (IPVIt == InputVector.end() - 1))// repeat, erase it (must be last entry
4142  {
4143  RepeatFlag = true;
4144  InputVector.erase(IPVIt);
4145  continue;
4146  }
4147  if(IPVIt == InputVector.begin()) // store for later then erase train data line
4148  {
4149  TrainDataLine = *IPVIt;
4150  InputVector.erase(IPVIt);
4151  break;
4152  }
4153  if(TrainController->SplitEntry(2, *IPVIt, GiveMessagesFalse, CheckLocationsExistInRailwayFalse, First, Second,
4154  Third, Fourth, RearStartOrRepeatMins, FrontStartOrRepeatDigits, FormatType,
4155  LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
4156  {
4157  if((!TrainController->CheckTimeValidity(34, First, TimeVal)) && (FormatType != FinRemHere)) //Frh has no time so don't want to erase this
4158  {
4159  InputVector.erase(IPVIt);
4160  continue;
4161  }
4162  if((FormatType == TimeLoc) || (FormatType == TimeTimeLoc) || (FormatType == PassTime))
4163  {
4164  continue;
4165  }
4166  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp") || (Second == "cdt") || (Second == "dsc") || (Second == "cms"))
4167  {
4168  continue;
4169  }
4170  if((SequenceType == FinishSequence) || (SequenceType == StartSequence))//keep these so have start & finish times to use (modified) as inverted finish & start times
4171  {
4172  continue;
4173  }
4174  InputVector.erase(IPVIt); //if none (should only be repeats & they are dealt with above, but keep for safety's sake) of above then erase
4175  continue;
4176  }
4177  else
4178  {
4179  InputVector.erase(IPVIt); //if fails when split then erase
4180  continue;
4181  }
4182  }
4183  }
4184  if(InputVector.size() == 0)
4185  {
4186  ShowMessage("Can't find any actions to invert!");
4187  Utilities->CallLogPop(2561);
4188  return;
4189  }
4190  if(InputVector.size() == 1)
4191  {
4192  ShowMessage("Only one action so there's nothing to invert!");
4193  Utilities->CallLogPop(2562);
4194  return;
4195  }
4196  //now have InputVector populated in order with legitimate entries & it isn't empty, change all TimeTimeLocs to 2 x TimeLocs & Finish to TimeLoc
4197 /*
4198 NewEntry = ""; //diagnostics
4199 for(TTEVIterator IPVIt = InputVector.begin(); IPVIt < InputVector.end(); IPVIt++)
4200 {
4201  NewEntry += *IPVIt + ',';
4202 }
4203 CopiedEntryStr = NewEntry; //this is needed for pasting as a new entry
4204 PasteTTEntryButton->Click(); //paste it after the current entry
4205 */
4206 
4207  while(true) //have to restart the search after every insertion as the vector may change location
4208  {
4209  bool TimeTimeLocFound = false;
4210  AnsiString LastTimeVal = "";
4211  for(TTEVIterator IPVIt = InputVector.begin(); IPVIt < InputVector.end(); IPVIt++)
4212  {
4213  if((*IPVIt)[1] == 'W') // warning
4214  {
4215  *IPVIt = (*IPVIt).SubString(2, (*IPVIt).Length() - 1);
4216  // strip it off
4217  }
4218  if(TrainController->SplitEntry(3, *IPVIt, GiveMessagesFalse, CheckLocationsExistInRailwayFalse, First, Second,
4219  Third, Fourth, RearStartOrRepeatMins, FrontStartOrRepeatDigits, FormatType,
4220  LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
4221  {
4222  if(FormatType == TimeTimeLoc)
4223  {
4224  LastTimeVal = Second; //if there's a Fourth then that's the min dwell time, don't carry it forwards
4225  AnsiString Arrival = First + ";" + Third;
4226  AnsiString Departure = Second + ";" + Third;
4227  *IPVIt = Departure; //substitute for original entry
4228  InputVector.insert(IPVIt, Arrival); //insertion is before current pointer position, hence arrival
4229  TimeTimeLocFound = true;
4230  break; //from for.. next.. loop
4231  }
4232  else if(SequenceType == StartSequence)
4233  {
4234  *IPVIt = First + ";<Service finish command>";
4235  LastTimeVal = First;
4236  continue; //to next IPVIt value
4237  }
4238  else if(FormatType == FinRemHere)
4239  {
4240  *IPVIt = LastTimeVal + ";<Service start command>";
4241  break; //from for.. next.. loop, will also break from While loop as TimeTimeLoc won't have been found
4242  }
4243  else if((SequenceType == FinishSequence) && (FormatType != FinRemHere))
4244  {
4245  *IPVIt = First + ";<Service start command>";
4246  break; //from for.. next.. loop, will also break from While loop as TimeTimeLoc won't have been found
4247  }
4248  else
4249  {
4250  LastTimeVal = First;
4251  continue; //to next IPVIt value
4252  }
4253  }
4254  else
4255  {
4256  //shouldn't reach here but if do then abort
4257  ShowMessage("Unable to invert this entry");
4258  Utilities->CallLogPop(2563);
4259  return;
4260  }
4261  }
4262  if(TimeTimeLocFound)
4263  {
4264  continue; //repeat the while loop
4265  }
4266  else
4267  {
4268  break; //from while loop
4269  }
4270  }
4271  //InputVector now complete
4272 
4273 /* //diagnostics
4274 NewEntry = ""; //start & finish added & all TimeLocs
4275 for(TTEVIterator IPVIt = InputVector.begin(); IPVIt < InputVector.end(); IPVIt++)
4276 {
4277  NewEntry += *IPVIt + ',';
4278 }
4279 CopiedEntryStr = NewEntry; //this is needed for pasting as a new entry
4280 PasteTTEntryButton->Click(); //paste it after the current entry
4281 */
4282 
4283  //populate OutputVector in reverse
4284  for(TTEVIterator IPVIt = InputVector.end() - 1; IPVIt >= InputVector.begin(); IPVIt--)
4285  {
4286  OutputVector.push_back(*IPVIt);
4287  }
4288  //extract all times in same order into TimeVector1
4289  for(TTEVIterator OPVIt = OutputVector.begin(); OPVIt < OutputVector.end(); OPVIt++)
4290  {
4291  TimeVector1.push_back((*OPVIt).SubString(1, 5));
4292  }
4293  //convert times to integers
4294  for(TTEVIterator TVIt = TimeVector1.begin(); TVIt < TimeVector1.end(); TVIt++)
4295  {
4296  int Mins, Hrs;
4297  Mins = (*TVIt).SubString(4, 2).ToInt();
4298  Hrs = (*TVIt).SubString(1, 2).ToInt();
4299  MinVector.push_back(Mins + (Hrs * 60));
4300  }
4301  //zero all wrt first time
4302  for(TMinVector::iterator MVIt = MinVector.begin(); MVIt < MinVector.end(); MVIt++)
4303  {
4304  int StartVal;
4305  if(MVIt == MinVector.begin())
4306  {
4307  StartVal = *MVIt;
4308  }
4309  *MVIt -= StartVal;
4310  }
4311  //change sign of all negative integers
4312  for(TMinVector::iterator MVIt = MinVector.begin(); MVIt < MinVector.end(); MVIt++)
4313  {
4314  *MVIt = abs(*MVIt);
4315  }
4316  //put these values back into OutVector after changing back to AnsiStrings
4317  for(unsigned int x = 0; x < MinVector.size(); x++)
4318  {
4319  AnsiString AnsiTimeVal = TrainController->MinsToAnsiTime(MinVector.at(x));
4320  AnsiString OutVal = OutputVector.at(x);
4321  AnsiString NewOutVal = AnsiTimeVal + OutputVector.at(x).SubString(6, OutputVector.at(x).Length() - 5);
4322  OutputVector.at(x) = NewOutVal;
4323  }
4324 /* //diagnostics
4325 NewEntry = "";
4326 for(TTEVIterator PVIt = OutputVector.begin(); PVIt < OutputVector.end(); PVIt++)
4327 {
4328  NewEntry += *PVIt + ',';
4329 }
4330 CopiedEntryStr = NewEntry; //this is needed for pasting as a new entry
4331 PasteTTEntryButton->Click(); //paste it after the current entry
4332 */
4333 
4334  //convert successive TimeLocs to TimeTimeLocs and substitute text for linked service reference
4335 // bool FirstTimeLocRegistered = false; } declared too deep in nested blocks
4336 // AnsiString ArrivalTime, DepartureTime, FirstTimeLoc, FirstLocation; } show as 'cannot access, out of scope' in debugger
4337  for(TTEVIterator OPVIt = OutputVector.begin(); OPVIt < OutputVector.end(); OPVIt++)
4338  {
4339  First = ""; Second = ""; Third = ""; Fourth = "";
4340  if(TrainController->SplitEntry(4, *OPVIt, GiveMessagesFalse, CheckLocationsExistInRailwayFalse, First, Second,
4341  Third, Fourth, RearStartOrRepeatMins, FrontStartOrRepeatDigits, FormatType,
4342  LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
4343  {
4344  if(FormatType == TimeLoc) //strip off any minimum dwell times //added at v2.23.0
4345  {
4346  if(Third != "") //there is a min dwell time
4347  {
4348  for(int x = (*OPVIt).Length(); x > 0; x--)
4349  {
4350  bool BreakDue = false;
4351  if((*OPVIt)[x] == ';')
4352  {
4353  BreakDue = true;
4354  }
4355  *OPVIt = OPVIt->SubString(1, x - 1);
4356  if(BreakDue)
4357  {
4358  break;
4359  }
4360  }
4361  }
4362  }
4363  if((FormatType != TimeLoc) || (OPVIt == OutputVector.begin()) || (OPVIt == OutputVector.end() - 1)) // first & last entries are start & finish but SplitEntry
4364  { //shows then as TimeLocs (Time;text) so capture them here
4365  if(FirstTimeLocRegistered)
4366  {
4367  OutputVector2.push_back(FirstTimeLoc); //push this first as not followed by a second TimeLoc
4368  }
4369  FirstTimeLocRegistered = false;
4370  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
4371  {
4372  OutputVector2.push_back(First + AnsiString(';') + Second + ";<linked service reference>");
4373  }
4374  else
4375  {
4376  OutputVector2.push_back(*OPVIt);
4377  }
4378  }
4379  else
4380  {
4381  if(!FirstTimeLocRegistered)
4382  {
4383  ArrivalTime = (*OPVIt).SubString(1, 5);
4384  FirstTimeLocRegistered = true;
4385  FirstTimeLoc = *OPVIt;
4386  FirstLocation = (*OPVIt).SubString(7, (*OPVIt).Length() - 6);
4387  }
4388  else
4389  {
4390  if(FirstLocation == (*OPVIt).SubString(7, (*OPVIt).Length() - 6)) //locations same
4391  {
4392  DepartureTime = (*OPVIt).SubString(1, 5);
4393  OutputVector2.push_back(ArrivalTime + AnsiString(';') + DepartureTime + AnsiString(';') + FirstLocation);
4394  FirstTimeLocRegistered = false;
4395  }
4396  else
4397  {
4398  OutputVector2.push_back(FirstTimeLoc); //earlier one was a TimeLoc on its own
4399  ArrivalTime = (*OPVIt).SubString(1, 5); //now register the current TimeLoc entry
4400  FirstTimeLocRegistered = true;
4401  FirstTimeLoc = *OPVIt;
4402  FirstLocation = (*OPVIt).SubString(7, (*OPVIt).Length() - 6);
4403  FirstTimeLocRegistered = true;
4404  }
4405  }
4406  }
4407  }
4408  }
4409  //add in train data & repeat command if RepeatFlag true
4410  AnsiString Remainder = "";
4411  int Pos = TrainDataLine.Pos(';'); //find first semicolon (will be after the service ref)
4412  TrainDataLine = TrainDataLine.SubString(Pos + 1, TrainDataLine.Length() - Pos); //chop off the service ref
4413  Pos = TrainDataLine.Pos(';'); //find semicolon after the description (if none then no train data)
4414  if((Pos != 0) && (Pos != TrainDataLine.Length())) //data present, if not then Remainder == ""
4415  {
4416  Remainder = TrainDataLine.SubString(Pos, TrainDataLine.Length() - Pos + 1); //chop off the description but leave the semicolon after it
4417  }
4418  TrainDataLine = "<Service ref>;<Description>" + Remainder;
4419  OutputVector2.insert(OutputVector2.begin(), TrainDataLine);
4420  if(RepeatFlag)
4421  {
4422  OutputVector2.insert(OutputVector2.end(), "<Repeat command if required>");
4423  }
4424  // convert to a timetable entry - single string
4425 
4426  NewEntry = "";
4427  for(TTEVIterator OPVIt = OutputVector2.begin(); OPVIt < OutputVector2.end(); OPVIt++)
4428  {
4429  NewEntry += *OPVIt + ',';
4430  }
4431 
4433  {
4434  ShowMessage("The events in the highlighted service will be listed in reverse order and pasted and highlighted as the next entry. "
4435  "Train data, transit and dwell times match the current entry but the start time is 00:00, ready to be adjusted using the 'Add mins' function "
4436  "with a value corresponding to the required start time in minutes.\n\nService reference, description, start, finish and repeat "
4437  "commands and linked service references remain to be added (indicated by '<...>'), and if this is to be a follow-on service then "
4438  "the train data must be removed.\n\nIf train descriptions or maximum speeds have been changed then the values will probably need to be "
4439  "amended to suit the inverted timetable.\n\nNote that if any minimum dwell times have been added to the original service then these will "
4440  "be stripped off and should be added again if required.\n\nThis message will not be shown again.");
4441  InvertTTEntryMessageSent = true;
4442  }
4443 
4444  CopiedEntryStr = NewEntry; //this is needed for pasting as a new entry
4445  PasteTTEntryButton->Click(); //paste it after the current entry
4446  Utilities->CallLogPop(2564);
4447  }
4448  }
4449  catch(const Exception &e) //non-error catch
4450  {
4451  ShowMessage("Unable to invert this entry. Error message = " + e.Message);
4452  Utilities->CallLogPop(2565);
4453  return;
4454  }
4455 }
4456 
4457 // ---------------------------------------------------------------------------
4458 
4459 void __fastcall TInterface::CutTTEntryButtonClick(TObject *Sender)
4460 {
4461  try
4462  {
4463  TrainController->LogEvent("CutTTEntryButtonClick");
4464  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CutTTEntryButtonClick");
4465  if(TTCurrentEntryIterator == TimetableEditVector.end()) // || (*TTCurrentEntryIterator == ""))//safeguard
4466  {
4467  Utilities->CallLogPop(1674);
4468  return;
4469  }
4471  CopiedEntryFlag = true;
4472  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // vector pointers unreliable after an erase,
4473  // so use the position in the vector
4475 // now need to rebuild all the pointers & the AllEntriesTTListBox so repeat the process from EditTimetableMenuItemClick
4476 // pick up the start time if there is one
4477  TimetableChangedFlag = true;
4478  TimetableValidFlag = false;
4479  TTEntryChangedFlag = false;
4480  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
4485  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4486  // position changing in AllEntriesTTListBox
4487  AllEntriesTTListBox->Clear();
4489  if(TimetableEditVector.empty())
4490  {
4492  SetLevel1Mode(109);
4493  Utilities->CallLogPop(1777);
4494  return;
4495  }
4496 // reset the TTCurrentEntryIterator to the Entry before the erased one if there is one //was 'after', changed at v2.5.0
4497 // but vector pointers unreliable after an erase, so use the position in the vector
4498  if(OldVectorPos == 0)
4499  {
4501  }
4502  else
4503  {
4504  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos - 1;
4505  }
4507  {
4508  OneEntryTimetableMemo->Clear();
4509  }
4511  SetLevel1Mode(115);
4512  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
4513  {
4515  }
4516  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
4517  {
4519  }
4520  else
4521  {
4522  AllEntriesTTListBox->TopIndex = TopPos;
4523  }
4524  Utilities->CallLogPop(1676);
4525  }
4526  catch(const Exception &e)
4527  {
4528  ErrorLog(111, e.Message);
4529  }
4530 }
4531 
4532 // ---------------------------------------------------------------------------
4533 void __fastcall TInterface::PasteTTEntryButtonClick(TObject *Sender)
4534 {
4535  try
4536  {
4537  TrainController->LogEvent("PasteTTEntryButtonClick");
4538  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PasteTTEntryButtonClick");
4539  if(TTCurrentEntryIterator == TimetableEditVector.end()) // || (CopiedEntryStr == "")) allow blank copies
4540  {
4541  Utilities->CallLogPop(1637);
4542  return;
4543  }
4544  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // vector pointers unreliable after an insert
4545  TimetableEditVector.insert(TTCurrentEntryIterator + 1, CopiedEntryStr); // inserts before the indicated pointer position, i.e. immediately
4546  // after the current Entry - may be at the end
4547  TimetableChangedFlag = true;
4548  TimetableValidFlag = false;
4549  TTEntryChangedFlag = false;
4550  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
4555  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4556  // position changing in AllEntriesTTListBox
4557  AllEntriesTTListBox->Clear();
4559  if(TimetableEditVector.empty())
4560  {
4562  SetLevel1Mode(110);
4563  Utilities->CallLogPop(1778);
4564  return;
4565  }
4566 // restore TTCurrentEntryIterator
4567  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
4568  TTCurrentEntryIterator++; // advance the pointer to the pasted entry
4569 // CopiedEntryStr = "";//revert to null - no, allow multiple copies
4571  SetLevel1Mode(94);
4572  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
4573  {
4575  }
4576  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
4577  {
4579  }
4580  else
4581  {
4582  AllEntriesTTListBox->TopIndex = TopPos;
4583  }
4584  Utilities->CallLogPop(1620);
4585  }
4586  catch(const Exception &e)
4587  {
4588  ErrorLog(57, e.Message);
4589  }
4590 }
4591 // ---------------------------------------------------------------------------
4592 
4593 void __fastcall TInterface::DeleteTTEntryButtonClick(TObject *Sender)
4594 {
4595  try
4596  {
4597  TrainController->LogEvent("DeleteTTEntryButtonClick");
4598  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",DeleteTTEntryButtonClick");
4600  {
4601  Utilities->CallLogPop(1645);
4602  return;
4603  }
4604  UnicodeString MessageStr = "Are you sure this entry should be deleted?";
4605  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
4606  if(button == IDNO)
4607  {
4608  Utilities->CallLogPop(1663);
4609  return;
4610  }
4611  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // vector pointers unreliable after an erase,
4612  // so use the position in the vector
4614 
4615 // now need to rebuild all the pointers & the AllEntriesTTListBox so repeat the process from EditTimetableMenuItemClick
4616 // pick up the start time if there is one
4617  TimetableChangedFlag = true;
4618  TimetableValidFlag = false;
4619  TTEntryChangedFlag = false;
4620  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
4625  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4626  // position changing in AllEntriesTTListBox
4627  AllEntriesTTListBox->Clear();
4629  if(TimetableEditVector.empty())
4630  {
4632  SetLevel1Mode(111);
4633  Utilities->CallLogPop(1779);
4634  return;
4635  }
4636 // reset the TTCurrentEntryIterator to the Entry before the erased one if there is one
4637 // but vector pointers unreliable after an erase, so use the position in the vector
4638  if(OldVectorPos == 0)
4639  {
4641  }
4642  else
4643  {
4644  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos - 1;
4645  }
4647  {
4648  OneEntryTimetableMemo->Clear();
4649  }
4651  SetLevel1Mode(95);
4652  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
4653  {
4655  }
4656  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
4657  {
4659  }
4660  else
4661  {
4662  AllEntriesTTListBox->TopIndex = TopPos;
4663  }
4664  Utilities->CallLogPop(1621);
4665  }
4666  catch(const Exception &e)
4667  {
4668  ErrorLog(58, e.Message);
4669  }
4670 }
4671 // ---------------------------------------------------------------------------
4672 
4673 void __fastcall TInterface::SaveTTEntryButtonClick(TObject *Sender)
4674 {
4675  try
4676  {
4677  TrainController->LogEvent("SaveTTEntryButtonClick");
4678  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveTTEntryButtonClick");
4679 /* allow blank lines to be saved
4680  AnsiString ContentStr = OneEntryTimetableMemo->Text;
4681  if((ContentStr == "\r\n") || (ContentStr == "\n") || (ContentStr == ""))
4682  {
4683  Utilities->CallLogPop(1679);
4684  return;
4685  }
4686 */
4687  AnsiString TempStr = "";
4688  bool ActiveLine = false;
4690  {
4691  if(*TTCurrentEntryIterator != "")
4692  {
4694  {
4695  ActiveLine = true;
4696  // need to add commas after each line in OneEntryTimetableMemo exept the last, where have '\0'
4697  for(int x = 0; x < OneEntryTimetableMemo->Lines->Count; x++)
4698  {
4699  for(int y = 1; y <= OneEntryTimetableMemo->Lines->Strings[x].Length(); y++)
4700  {
4701  TempStr += OneEntryTimetableMemo->Lines->Strings[x][y];
4702  }
4703  if(x < (OneEntryTimetableMemo->Lines->Count - 1))
4704  {
4705  TempStr += ',';
4706  }
4707  // No need to add a '\n' as a '\0' is added automatically as a string delimiter. If add '\n' then it is treated as a blank line and
4708  // ends the timetable
4709  }
4710  // strip any excess commas from the end
4711  if(TempStr != "")
4712  {
4713  while(TempStr[TempStr.Length()] == ',')
4714  {
4715  TempStr = TempStr.SubString(1, TempStr.Length() - 1);
4716  if(TempStr == "")
4717  {
4718  break;
4719  }
4720  }
4721  }
4722  }
4723  }
4724  }
4725  if(!ActiveLine)
4726  {
4727  TempStr = OneEntryTimetableMemo->Text; // Note that if the entry was intended as a service but goes in as plain text because
4728  // the service & entry pointers aren't yet set, then CRLFs will be converted to commas in
4729  // CompileAllEntriesMemoAndSetIterators if it appears after the start time
4730  // and before a blank line or end of file, so the syntax check will work OK
4731  }
4732  if(AZOrderButton->Caption == AnsiString("Original Order"))
4733  {
4735  }
4736  if(TimeOrderButton->Caption == AnsiString("Original Order"))
4737  {
4739  }
4740  TimetableValidFlag = false;
4741  TimetableChangedFlag = true;
4742  TTEntryChangedFlag = false;
4743  int TopPos;
4745  {
4747  }
4749  {
4750  (*TTCurrentEntryIterator) = TempStr;
4751  // need to reset the AllEntriesTTListBox in case the headcode has changed
4752  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // vector pointers unreliable after an insert
4753  TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4754  // position changing in AllEntriesTTListBox
4755  AllEntriesTTListBox->Clear();
4757  if(TimetableEditVector.empty())
4758  {
4760  SetLevel1Mode(112);
4761  Utilities->CallLogPop(1780);
4762  return;
4763  }
4764  // restore TTCurrentEntryIterator
4765  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
4766  }
4767  else
4768  {
4769  NewEntryInPreparationFlag = false;
4771  {
4772  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // vector pointers unreliable after an insert
4773  TimetableEditVector.insert(TTCurrentEntryIterator + 1, TempStr); // inserts before the indicated pointer position, which may be at the end
4774  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
4776  }
4777  else
4778  {
4779  TimetableEditVector.insert(TimetableEditVector.end(), TempStr); // inserts before the indicated pointer position
4781  }
4782  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // save the current position
4783  TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
4784  // position changing in AllEntriesTTListBox
4785  AllEntriesTTListBox->Clear();
4787  if(TimetableEditVector.empty())
4788  {
4790  SetLevel1Mode(113);
4791  Utilities->CallLogPop(1781);
4792  return;
4793  }
4794 // reset the TTCurrentEntryIterator after CompileAllEntriesMemoAndSetIterators
4795  if(OldVectorPos >= TimetableEditVector.end() - TimetableEditVector.begin() - 1)
4796  {
4798  }
4799  else
4800  {
4801  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
4802  }
4803  }
4805  SetLevel1Mode(96);
4806  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
4807  {
4809  }
4810  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
4811  {
4813  }
4814  else
4815  {
4816  AllEntriesTTListBox->TopIndex = TopPos;
4817  }
4818  Utilities->CallLogPop(1622);
4819  }
4820  catch(const Exception &e)
4821  {
4822  ErrorLog(59, e.Message);
4823  }
4824 }
4825 // ---------------------------------------------------------------------------
4826 
4827 void __fastcall TInterface::SaveTTButtonClick(TObject *Sender)
4828 {
4829  try
4830  {
4831  TrainController->LogEvent("SaveTTButtonClick");
4832  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveTTButtonClick");
4833  if(TimetableEditVector.empty())
4834  {
4835  ShowMessage("Timetable is empty, can't save an empty timetable");
4836  Utilities->CallLogPop(1685);
4837  return;
4838  }
4839  if(AZOrderButton->Caption == AnsiString("Original Order")) //new at v2.15.1
4840  {
4841  UnicodeString MessageStr =
4842  "This will save the timetable in alphabetical order and the original order will be lost. If this is what is required "
4843  "click 'YES' and this warning will not be shown again, but if it isn't click 'NO'.\n\nIf a new entry has been created it can "
4844  "be copied (to avoid losing it) before reverting to the original order and then it can be pasted and saved.\n\n"
4845  "If the alphabetical order is saved it will become the new original order.";
4847  {
4848  int button = Application->MessageBox(MessageStr.c_str(), L"Please Note:", MB_YESNO | MB_ICONWARNING);
4849  if(button == IDNO)
4850  {
4851  Utilities->CallLogPop(2625);
4852  return;
4853  }
4855  }
4856  }
4857  AZOrderButton->Caption = AnsiString("A-Z Order");
4858  if(TimeOrderButton->Caption == AnsiString("Original Order")) //new at v2.18.0
4859  {
4860  UnicodeString MessageStr =
4861  "This will save the timetable in chronological (time) order and the original order will be lost. If this is what is required "
4862  "click 'YES' and this warning will not be shown again, but if it isn't click 'NO'.\n\nIf a new entry has been created it can "
4863  "be copied (to avoid losing it) before reverting to the original order and then it can be pasted and saved.\n\n"
4864  "If the chronological (time) order is saved it will become the new original order.";
4866  {
4867  int button = Application->MessageBox(MessageStr.c_str(), L"Please Note:", MB_YESNO | MB_ICONWARNING);
4868  if(button == IDNO)
4869  {
4870  Utilities->CallLogPop(2690);
4871  return;
4872  }
4874  }
4875  }
4876  TimeOrderButton->Caption = AnsiString("Time Order");
4877  std::ofstream TTBLFile;
4878  if(CreateEditTTFileName != "")
4879  {
4880  TTBLFile.open(CreateEditTTFileName.c_str(), std::ios_base::binary); // if text then each time sees a "\r\n" pair enters "\r\n\n" because '\n'
4881  // on its own causes "\r\n' to ne inserted, binary just enters characters as they are
4882  }
4883  else
4884  {
4885  if(SaveTTDialog->Execute())
4886  {
4887  if(SaveTTDialog->InitialDir != TPath::GetDirectoryName(SaveTTDialog->FileName)) // new at v2.6.0 to retain a new directory
4888  {
4889  TimetableDialog->InitialDir = TPath::GetDirectoryName(SaveTTDialog->FileName);
4890  SaveTTDialog->InitialDir = TPath::GetDirectoryName(SaveTTDialog->FileName);
4891  }
4892  CreateEditTTFileName = AnsiString(SaveTTDialog->FileName);
4893  for(int x = CreateEditTTFileName.Length(); x > 0; x--)
4894  {
4895  if(CreateEditTTFileName[x] == '\\')
4896  {
4897  CreateEditTTTitle = CreateEditTTFileName.SubString(x + 1, CreateEditTTFileName.Length() - x - 4);
4898  break;
4899  }
4900  }
4901  TTBLFile.open(CreateEditTTFileName.c_str(), std::ios_base::binary); // if text then each time sees a "\r\n" pair enters "\r\n\n" because '\n'
4902  // on its own causes "\r\n' to ne inserted, binary just enters characters as they are
4903  }
4904  else // cancelled dialog
4905  {
4907  SetLevel1Mode(137);
4908  Utilities->CallLogPop(2205);
4909  return;
4910  }
4911  }
4912  if(TTBLFile.is_open())
4913  {
4914 /* dropped this as may cause confusion for users, can assume timetable built with same version as railway
4915  if(TimetableEditVector.front() != "Version = " + ProgramVersion) //added at v2.19.1 to add version as 1st line of file
4916  {
4917  if((TimetableEditVector.front().SubString(1, 10)) != "Version = ") //earlier version not recorded, so add at start
4918  {
4919  TimetableEditVector.insert(TimetableEditVector.begin(), "Version = " + ProgramVersion);
4920  }
4921  else //an earlier version has been recorded so replace it
4922  {
4923  TimetableEditVector.front() = "Version = " + ProgramVersion;
4924  }
4925  }
4926 */
4928  {
4929  TTBLFile << (*TEVIterator).c_str() << '\0';
4930  }
4931  TimetableChangedFlag = false;
4932  TTBLFile.close();
4933  }
4934  else
4935  {
4936  ShowMessage(CreateEditTTFileName + " failed to open, ensure not already open in another application");
4937  }
4938 // CompileAllEntriesMemoAndSetIterators(); //added at v2.19.1 - this and next line highlight the first version name entry [dropped when
4939 // TTCurrentEntryIterator = TimetableEditVector.begin(); version no. inclusion dropped]
4941  SetLevel1Mode(97);
4942  Utilities->CallLogPop(1623);
4943  }
4944  catch(const Exception &e) //non-error catch
4945  {
4946  TrainController->StopTTClockMessage(136, "Timetable failed to save\nError message: " + e.Message); //added at v2.15.0
4947  Screen->Cursor = TCursor(-2); // Arrow;
4948 // ErrorLog(60, e.Message);
4949  }
4950 }
4951 // ---------------------------------------------------------------------------
4952 
4953 void __fastcall TInterface::SaveTTAsButtonClick(TObject *Sender)
4954 {
4955  try
4956  {
4957  TrainController->LogEvent("SaveTTAsButtonClick");
4958  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveTTAsButtonClick");
4959  if(TimetableEditVector.empty())
4960  {
4961  ShowMessage("Timetable is empty, can't save an empty timetable");
4962  Utilities->CallLogPop(1686);
4963  return;
4964  }
4965  std::ofstream TTBLFile;
4966  if(SaveTTDialog->Execute())
4967  {
4968  if(SaveTTDialog->InitialDir != TPath::GetDirectoryName(SaveTTDialog->FileName)) // new at v2.6.0 to retain a new directory
4969  {
4970  TimetableDialog->InitialDir = TPath::GetDirectoryName(SaveTTDialog->FileName);
4971  SaveTTDialog->InitialDir = TPath::GetDirectoryName(SaveTTDialog->FileName);
4972  }
4973  CreateEditTTFileName = SaveTTDialog->FileName;
4974  for(int x = SaveTTDialog->FileName.Length(); x > 0; x--)
4975  {
4976  if(SaveTTDialog->FileName[x] == '\\')
4977  {
4978  CreateEditTTTitle = SaveTTDialog->FileName.SubString(x + 1, SaveTTDialog->FileName.Length() - x - 4);
4979  break;
4980  }
4981  }
4982  TTBLFile.open(CreateEditTTFileName.c_str(), std::ios_base::binary); // if text then each time sees a "\r\n" pair enters "\r\n\n" because '\n'
4983  // on its own causes "\r\n' to ne inserted, binary just enters characters as they are
4984  }
4985  else // cancelled dialog
4986  {
4988  SetLevel1Mode(138);
4989  Utilities->CallLogPop(2206);
4990  return;
4991  }
4992  if(TTBLFile.is_open())
4993  {
4994 /* dropped this as may cause confusion for users, can assume timetable built with same version as railway
4995  if(TimetableEditVector.front() != "Version = " + ProgramVersion) //added at v2.19.1 to add version as 1st line of file
4996  {
4997  if((TimetableEditVector.front().SubString(1, 10)) != "Version = ") //earlier version not recorded, so add at start
4998  {
4999  TimetableEditVector.insert(TimetableEditVector.begin(), "Version = " + ProgramVersion);
5000  }
5001  else //an earlier version has been recorded so replace it
5002  {
5003  TimetableEditVector.front() = "Version = " + ProgramVersion;
5004  }
5005  }
5006 */
5008  {
5009  TTBLFile << (*TEVIterator).c_str() << '\0';
5010  }
5011  TimetableChangedFlag = false;
5012  TTBLFile.close();
5013  }
5014  else
5015  {
5016  ShowMessage(CreateEditTTFileName + " failed to open, ensure not already open in another application");
5017  }
5018 // CompileAllEntriesMemoAndSetIterators(); //added at v2.19.1 - this and next line highlight the first version name entry [dropped when
5019 // TTCurrentEntryIterator = TimetableEditVector.begin(); version no. inclusion dropped]
5021  SetLevel1Mode(117);
5022  Utilities->CallLogPop(1667);
5023  }
5024  catch(const Exception &e)
5025  {
5026  ErrorLog(108, e.Message);
5027  }
5028 }
5029 // ---------------------------------------------------------------------------
5030 
5031 void __fastcall TInterface::TTServiceSyntaxCheckButtonClick(TObject *Sender)
5032 {
5033  try
5034  {
5035  TrainController->LogEvent("TTServiceSyntaxCheckButtonClick");
5036  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTServiceSyntaxCheckButtonClick");
5037  int Count = 1; // anything > 0 OK as if 0 still seeking a start time
5038  bool EndOfFile = false;
5039  bool FinalCallFalse = false;
5040  bool GiveMessagesTrue = true;
5041  bool CheckLocationsExistInRailway = false;
5042  if(RlyFile)
5043  {
5044  CheckLocationsExistInRailway = true;
5045  }
5046 // TrainController->AnyHeadCodeValid = true; //don't fail here because of an unrestricted headcode, if no good will find when validate (dropped at v0.6b)
5047  if(TrainController->ProcessOneTimetableLine(2, Count, *TTCurrentEntryIterator, EndOfFile, FinalCallFalse, GiveMessagesTrue, CheckLocationsExistInRailway))
5048  // return true for success
5049  {
5050  ShowMessage(
5051  "The basic syntax seems OK but this check is very limited. Other aspects can only be checked by validating the whole timetable with the appropriate railway (.rly) loaded");
5052  }
5054  SetLevel1Mode(98);
5055  Utilities->CallLogPop(1624);
5056  }
5057  catch(const Exception &e)
5058  {
5059  ErrorLog(61, e.Message);
5060  }
5061 }
5062 // ---------------------------------------------------------------------------
5063 
5064 void __fastcall TInterface::ValidateTimetableButtonClick(TObject *Sender)
5065 {
5066  try
5067  {
5068  TrainController->LogEvent("ValidateTimetableButtonClick");
5069  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ValidateTimetableButtonClick");
5070  // reset all message flags, stops them being given twice new at v2.4.0
5071  TrainController->SSHigh = false;
5072 // TrainController->MRSHigh = false; removed at v2.21.0
5073 // TrainController->MRSLow = false;
5074  TrainController->MassHigh = false;
5075  TrainController->BFHigh = false;
5076  TrainController->BFLow = false;
5077  TrainController->PwrHigh = false;
5078  TrainController->SigSHigh = false;
5079  TrainController->SigSLow = false;
5080  if(CreateEditTTFileName == "")
5081  {
5082  Utilities->CallLogPop(1664);
5083  return;
5084  }
5085  bool CheckLocationsExistInRailwayTrue = true;
5086  if(TrainController->TimetableIntegrityCheck(2, CreateEditTTFileName.c_str(), true, CheckLocationsExistInRailwayTrue)) // messages = true
5087  {
5088  Screen->Cursor = TCursor(-11); // Hourglass;
5089  std::ifstream TTBLFile(CreateEditTTFileName.c_str(), std::ios_base::binary);
5090  if(TTBLFile.is_open())
5091  {
5092  if(BuildTrainDataVectorForValidateFile(0, TTBLFile, true, CheckLocationsExistInRailwayTrue)) // messages = true
5093  {
5094  if(!TwoLocationNamePanel->Visible) //added at v2.9.1. If this visible don't override it with another message.
5095  {
5096  ShowMessage("Timetable integrity OK");
5097  TimetableValidFlag = true;
5098  }
5099 // TrainController->TrainDataVector.clear(); keep this so can export a formatted tt
5100  }
5101  }
5102  else
5103  {
5104  ShowMessage("Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
5105  }
5106  Screen->Cursor = TCursor(-2); // Arrow
5107  } // if(TimetableIntegrityCheck
5108  else
5109  {
5110 // ShowMessage("Timetable preliminary integrity check failed"); dropped in v2.4.0 as messages given in all called functions
5111  }
5113  SetLevel1Mode(99);
5114  Utilities->CallLogPop(1625);
5115  }
5116  catch(const Exception &e)
5117  {
5118  ErrorLog(62, e.Message);
5119  }
5120 }
5121 
5122 // ---------------------------------------------------------------------------
5123 void __fastcall TInterface::MoveTTEntryUpButtonClick(TObject *Sender)
5124 {
5125  try
5126  {
5127  TrainController->LogEvent("MoveTTEntryUpButtonClick");
5128  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MoveTTEntryUpButtonClick");
5130  {
5131  Utilities->CallLogPop(1634);
5132  return;
5133  }
5134  if(TTCurrentEntryIterator < (TimetableEditVector.begin() + 1)) // shouldn't reach here but return if do
5135  {
5136  Utilities->CallLogPop(1632);
5137  return;
5138  }
5139  TEVIterator = TTCurrentEntryIterator - 1; // find earlier Entry
5140  AnsiString TempStr = *TEVIterator;
5142  *TTCurrentEntryIterator = TempStr;
5144  TimetableChangedFlag = true;
5145  TimetableValidFlag = false;
5146 
5147 // now rebuild AllEntriesTTListBox, DisplayOneTTLineInPanel will highlight it
5148  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
5149  // position changing in AllEntriesTTListBox
5150  AllEntriesTTListBox->Clear();
5151  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // save the old position
5153 // reset the TTCurrentEntryIterator after CompileAllEntriesMemoAndSetIterators
5154  if(OldVectorPos >= TimetableEditVector.end() - TimetableEditVector.begin() - 1)
5155  {
5157  }
5158  else
5159  {
5160  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
5161  }
5163  SetLevel1Mode(100);
5164  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
5165  {
5167  }
5168  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
5169  {
5171  }
5172  else
5173  {
5174  AllEntriesTTListBox->TopIndex = TopPos;
5175  }
5176  Utilities->CallLogPop(1626);
5177  }
5178  catch(const Exception &e)
5179  {
5180  ErrorLog(63, e.Message);
5181  }
5182 }
5183 // ---------------------------------------------------------------------------
5184 
5185 void __fastcall TInterface::MoveTTEntryDownButtonClick(TObject *Sender)
5186 {
5187  try
5188  {
5189  TrainController->LogEvent("MoveTTEntryDownButtonClick");
5190  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MoveTTEntryDownButtonClick");
5192  {
5193  Utilities->CallLogPop(1635);
5194  return;
5195  }
5196  if(TTCurrentEntryIterator >= (TimetableEditVector.end() - 1)) // shouldn't reach here but return if do
5197  {
5198  Utilities->CallLogPop(1678);
5199  return;
5200  }
5201  TEVIterator = TTCurrentEntryIterator + 1; // find later Entry
5202  AnsiString TempStr = *TEVIterator;
5204  *TTCurrentEntryIterator = TempStr;
5206  TimetableChangedFlag = true;
5207  TimetableValidFlag = false;
5208 
5209 // now rebuild AllEntriesTTListBox, DisplayOneTTLineInPanel will highlight it
5210  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
5211  // position changing in AllEntriesTTListBox
5212  AllEntriesTTListBox->Clear();
5213  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // save the old position
5215 // reset the TTCurrentEntryIterator after CompileAllEntriesMemoAndSetIterators
5216  if(OldVectorPos >= TimetableEditVector.end() - TimetableEditVector.begin() - 1)
5217  {
5219  }
5220  else
5221  {
5222  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
5223  }
5225  SetLevel1Mode(101);
5226  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
5227  {
5229  }
5230  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
5231  {
5233  }
5234  else
5235  {
5236  AllEntriesTTListBox->TopIndex = TopPos;
5237  }
5238  Utilities->CallLogPop(1627);
5239  }
5240  catch(const Exception &e)
5241  {
5242  ErrorLog(64, e.Message);
5243  }
5244 }
5245 
5246 // ---------------------------------------------------------------------------
5247 void __fastcall TInterface::CancelTTEntryButtonClick(TObject *Sender)
5248 {
5249  try
5250  {
5251  TrainController->LogEvent("CancelTTActionButtonClick");
5252  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CancelTTActionButtonClick");
5253  TTEntryChangedFlag = false;
5255  {
5256  NewEntryInPreparationFlag = false;
5257  OneEntryTimetableMemo->Clear();
5258  }
5259  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
5260  // position changing in AllEntriesTTListBox
5262  SetLevel1Mode(102);
5263  if((TTCurrentEntryIterator - TimetableEditVector.begin()) < TopPos)
5264  {
5266  }
5267  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (TopPos + 45))
5268  {
5270  }
5271  else
5272  {
5273  AllEntriesTTListBox->TopIndex = TopPos;
5274  }
5275  Utilities->CallLogPop(1630);
5276  }
5277  catch(const Exception &e)
5278  {
5279  ErrorLog(102, e.Message);
5280  }
5281 }
5282 
5283 // ---------------------------------------------------------------------------
5284 void __fastcall TInterface::RestoreTTButtonClick(TObject *Sender)
5285 {
5286  try
5287  {
5288  TrainController->LogEvent("RestoreTTButtonClick");
5289  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RestoreTTButtonClick");
5291  {
5292  UnicodeString MessageStr = "All changes to the timetable will be lost - proceed?";
5293  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
5294  if(button == IDNO)
5295  {
5296  Utilities->CallLogPop(1651);
5297  return;
5298  }
5299  }
5300  // repeat from EditTimetableMenuItemClick, but no need to check for non-ascii characters
5301  // open in binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
5302  std::ifstream TTBLFile(CreateEditTTFileName.c_str(), std::ios_base::binary);
5303  if(TTBLFile.is_open())
5304  {
5305  TimetableChangedFlag = false;
5306  TimetableValidFlag = false;
5307  TTEntryChangedFlag = false;
5308  NewEntryInPreparationFlag = false;
5309  CopiedEntryFlag = false;
5310  CopiedEntryStr = "";
5311  TimetableEditVector.clear();
5312  OneEntryTimetableMemo->Clear();
5313  AllEntriesTTListBox->Clear();
5314  TTStartTimeBox->Text = "";
5315  AddSubMinsBox->Text = "";
5316  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
5321  char *TimetableEntryString = new char[10000];
5322  while(true)
5323  {
5324  TTBLFile.getline(TimetableEntryString, 10000, '\0'); // pick up the entire AnsiString, including any embedded newlines
5325  if(TTBLFile.eof() && (TimetableEntryString[0] == '\0')) // stores a null in 1st position if doesn't load any characters
5326  {
5327  // may still have eof even if read a line, and
5328  // if so need to process it
5329  break;
5330  }
5331  AnsiString OneLine(TimetableEntryString);
5332  TimetableEditVector.push_back(OneLine);
5333  }
5334  TTBLFile.close();
5335  delete[]TimetableEntryString;
5336  // here with TimetableEditVector compiled
5337  }
5338  else
5339  {
5340  ShowMessage("Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
5341  Utilities->CallLogPop(1655);
5342  return;
5343  }
5345  if(TimetableEditVector.empty())
5346  {
5348  SetLevel1Mode(114);
5349  Utilities->CallLogPop(1782);
5350  return;
5351  }
5352 // all now set where can be
5354 // end of repeat from EditTimetableMenuItemClick
5355 
5357  SetLevel1Mode(104);
5358  Utilities->CallLogPop(1652);
5359  }
5360  catch(const Exception &e)
5361  {
5362  ErrorLog(104, e.Message);
5363  }
5364 }
5365 
5366 // ---------------------------------------------------------------------------
5367 void __fastcall TInterface::ExportTTButtonClick(TObject *Sender)
5368 {
5369  try
5370  {
5371  TrainController->LogEvent("ExportTTButtonClick");
5372  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExportTTButtonClick");
5373  if(!DirectoryExists(CurDir + "\\" + FORMATTEDTT_DIR_NAME))
5374  {
5375  ShowMessage("Failed to find folder " + FORMATTEDTT_DIR_NAME + " in the folder where 'railway.exe' resides. Timetable can't be exported");
5376  Utilities->CallLogPop(1698);
5377  return;
5378  }
5379 // Screen->Cursor = TCursor(-11); // Hourglass; - no good setting here as it's reset by ShowMessage in CreateFormattedTimetable. It's set there after
5380 // the message instead, but reset here afterwards
5381  AnsiString TTTitle;
5383  {
5384  for(int x = CreateEditTTFileName.Length(); x > 0; x--) // first need to strip out the timetable title from the full name
5385  {
5386  if(CreateEditTTFileName[x] == '\\')
5387  {
5388  TTTitle = CreateEditTTFileName.SubString(x + 1, CreateEditTTFileName.Length() - x - 4);
5389  break;
5390  }
5391  }
5393  }
5394  Screen->Cursor = TCursor(-2); // Arrow - reset after above function returns
5396  SetLevel1Mode(116);
5397  Utilities->CallLogPop(1662);
5398  }
5399  catch(const Exception &e)
5400  {
5401  ErrorLog(107, e.Message);
5402  }
5403 }
5404 
5405 // ---------------------------------------------------------------------------
5406 void __fastcall TInterface::TTTextButtonClick(TObject *Sender)
5407 {
5408  try
5409  {
5410  TrainController->LogEvent("TTTextButtonClick");
5411  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTTextButtonClick");
5412 /*
5413  if(TTStartTimeIterator == 0)
5414  {
5415  OneEntryTimetableMemo->Clear();
5416  TTStartTimeBox->SetFocus();
5417  Utilities->CallLogPop(1673);
5418  return;
5419  }
5420 */
5421  int SelPos = OneEntryTimetableMemo->SelStart;
5422  AnsiString FirstPart = OneEntryTimetableMemo->Text.SubString(1, SelPos);
5423  AnsiString LastPart = OneEntryTimetableMemo->Text.SubString(SelPos + 1, OneEntryTimetableMemo->Text.Length() - SelPos);
5424  OneEntryTimetableMemo->Text = FirstPart + ((TButton*)Sender)->Caption + LastPart;
5425  OneEntryTimetableMemo->SelStart = SelPos + ((TButton*)Sender)->Caption.Length();
5426  TTEntryChangedFlag = true;
5427  OneEntryTimetableMemo->SetFocus();
5429  SetLevel1Mode(119);
5430  Utilities->CallLogPop(1672);
5431  }
5432  catch(const Exception &e)
5433  {
5434  ErrorLog(110, e.Message);
5435  }
5436 }
5437 
5438 // ---------------------------------------------------------------------------
5439 void __fastcall TInterface::ExitTTModeButtonClick(TObject *Sender)
5440 {
5441  try
5442  {
5443  TrainController->LogEvent("ExitTTCreateEditButtonClick");
5444  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExitTTCreateEditButtonClick");
5446  {
5447  UnicodeString MessageStr = "The timetable has changed.\n\nAre you sure you want to exit without saving it?";
5448  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
5449  if(button == IDNO)
5450  {
5451  Utilities->CallLogPop(1603);
5452  return;
5453  }
5454  }
5455  TimetableChangedFlag = false;
5456  CreateEditTTFileName = ""; // set to null to allow a check during error file saving, if not null save the tt being edited to the file
5457  // added for Beta v0.2b
5458  CreateEditTTTitle = ""; // as above
5459  ConflictPanel->Visible = false;
5460  Level1Mode = BaseMode;
5461  SetLevel1Mode(84);
5462  Utilities->CallLogPop(1606);
5463  }
5464  catch(const Exception &e)
5465  {
5466  ErrorLog(49, e.Message);
5467  }
5468 }
5469 
5470 // ---------------------------------------------------------------------------
5471 
5472 void __fastcall TInterface::ExpandRepeatsButtonClick(TObject *Sender) //added at v2.17.0
5473 {
5474 /*The expand button is only enabled when a repeat service is highlighted.
5475 A warning message is given first with an option to quit about ensuring unique service refs when expanded (i.e. repeats allow xero for digit
5476 increases but expansion don't if there are linked services, and about using 'save as' rather than 'save' as when the expanded timetable is saved
5477 the earlier one is lost.
5478 The method is to compile a list of all linked repeat services (explained later), add a marker to the start of each linked service in the
5479 TimetableEditVector (to identify those to be expanded) then to expand them all.
5480 The TimetableEditVector contains all entries in the timetable editor including pre-start comments, start time, services,
5481 and comments within services. If there are post-service comments then it includes the blank line and the comments.
5482 In order to compile the list of linked repeat services two lists of TrainDataEntry pointers are used, the first (PreRepeatList) of services
5483 that haven't yet been examined for further links, and the second (RepeatList)of examined services. These use TrainDataEntry pointers rather than
5484 iterators because the links in the ActionVector take this form. Newly found links are pushed to the back of the PreRepeatList and links the be
5485 examined are taken from the front, so eventually that list will empty and the RepeatList will become full of all the linked services.
5486 
5487 But, since services don't have TimetableEditVector pointer values corresponding to TrainDataEntry pointers, first a map is contructed
5488 with keys as TrainDataEntry pointers (not iterators) and values as TimetableEditVector pointers, and that used to identify the correspondences.
5489 */
5490  try
5491  {
5492  TrainController->LogEvent("ExpandRepeatsButtonClick");
5493  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExpandRepeatsButtonClick");
5494 
5495  UnicodeString MessageStr = UnicodeString("This will create separate services from the selected repeat entry. Beware that if the repeating "
5496  "service contains links to another service and has zero as the headcode digit increase for each repeat "
5497  "(i.e. repeats all have the same headcode) then the expanded timetable will fail to validate. "
5498  "In these circumstances please ensure that the headcode changes for each repeat and that all the "
5499  "service references (not necessarily headcodes) for all linked services are unique.\n\n"
5500  "It is recommended to save the expanded timetable using 'Save timetable as' rather than 'Save "
5501  "timetable' in order to keep the original timetable in case the expansion does not provide what "
5502  "is wanted.\n\nContinue?");
5503  int button = Application->MessageBox(MessageStr.c_str(), L"Warning!", MB_YESNO | MB_ICONWARNING);
5504  if(button == IDNO)
5505  {
5506  Utilities->CallLogPop(2629);
5507  return;
5508  }
5509 
5510  std::list<TTrainDataEntry*> PreRepeatList;
5511 //List of linked repeat service pointers (so they are unique) prior to being examined for other linked services (expands when examining services
5512 //and contracts when services examined) - used when expanding repeat services
5513  std::list<TTrainDataEntry*> RepeatList;
5514 //<List of linked repeat service pointers after having been examined for other linked services (starts empty and expands when services examined) -
5515 //used when expanding repeat services
5516  std::list<TTrainDataEntry*>::iterator ListIt;
5517  AnsiString CurrentEntry = *TTCurrentEntryIterator;
5518 
5520  {
5521  Utilities->CallLogPop(2630);
5522  return;
5523  }
5524  PreRepeatList.clear();
5525  RepeatList.clear();
5526  if(!TimetableValidFlag) //button shouldn't be enabled if invalid but leave for safety
5527  {
5528  ShowMessage("Timetable must be validated before repeat services can be expanded");
5529  Utilities->CallLogPop(2631);
5530  return;
5531  }
5532  if((*TTCurrentEntryIterator).Pos(",R;") == 0) //button shouldn't be enabled if not a repeat but leave for safety
5533  {
5534  ShowMessage("Selected timetable entry is not a repeating service");
5535  Utilities->CallLogPop(2632);
5536  return;
5537  }
5538 
5539 //Build the map of corresponding TrainDataVector pointers (keys) and TimetableEditVector pointers (values) for all services.
5540  std::map<TTrainDataEntry*, AnsiString*> LinkMap;
5541  std::pair<TTrainDataEntry*, AnsiString*> LinkMapEntry;
5542  TTrainDataEntry* TDEPtr = &TrainController->TrainDataVector.front();
5543  bool StartTimeFound = false;
5544  TDateTime DummyTime; //required for CheckTimeValidity function, not used otherwise
5545  bool BreakFlag = false;
5546  bool BlankLineFound = false;
5547  int Mins;
5548  int Digits;
5549  int Repeats;
5550 
5551  int RpIndex = TTCurrentEntryIterator->Pos(",R;"); //RpIndex is the position of the comma
5552  if(RpIndex == 0) //can't find the repeat, give message & exit
5553  {
5554  ShowMessage("Failed to find a repeat for the selected entry. Please discard the modified timetable"
5555  " which is likely to be corrupt and reload the original.");
5556  Utilities->CallLogPop(2633);
5557  return;
5558  }
5559  AnsiString Rpt = TTCurrentEntryIterator->SubString(RpIndex + 3, TTCurrentEntryIterator->Length() - (RpIndex + 2)); //Rpt = "mins;digits;repeats"
5560  AnsiString AnsiMins = "", AnsiDigits = "", AnsiRepeats = "";
5561  int x = 1;
5562  while(Rpt[x] != ';')
5563  {
5564  AnsiMins += Rpt[x];
5565  x++;
5566  }
5567  x++;
5568  while(Rpt[x] != ';')
5569  {
5570  AnsiDigits += Rpt[x];
5571  x++;
5572  }
5573  x++;
5574  while(x <= Rpt.Length())
5575  {
5576  AnsiRepeats += Rpt[x];
5577  x++;
5578  }
5579  Mins = AnsiMins.ToInt();
5580  Digits = AnsiDigits.ToInt();
5581  Repeats = AnsiRepeats.ToInt();
5582 
5583  for(TTimetableEditVector::iterator TTEVIt = TimetableEditVector.begin(); TTEVIt != TimetableEditVector.end(); TTEVIt++)
5584  {
5585  while(!StartTimeFound && !TrainController->CheckTimeValidity(36, *TTEVIt, DummyTime))
5586  {
5587  BreakFlag = true;
5588  break;
5589  }
5590  if(BreakFlag)
5591  {
5592  BreakFlag = false;
5593  continue;
5594  }
5595  StartTimeFound = true;
5596  if(TrainController->CheckTimeValidity(37, *TTEVIt, DummyTime)) //step past start time
5597  {
5598  continue; //no subsequent entry within the timetable can start with a time or it wouldn't validate
5599  }
5600  if(*TTEVIt == "")
5601  {
5602  BlankLineFound = true; //if there is no blank line then the for... loop will terminate at last service or comment
5603  }
5604  if((!BlankLineFound && (*TTEVIt)[1] != '*'))
5605  { //valid service line
5606  LinkMapEntry.first = TDEPtr;
5607  LinkMapEntry.second = &(*TTEVIt); //convert iterator to AnsiString pointer
5608  LinkMap.insert(LinkMapEntry);
5609  TDEPtr++; //ready for next insert
5610  }
5611  }
5612  /*Now need to get the TrainDataEntry pointer value for the highlighted entry (pointed to by TTCurrentEntryIterator)
5613  TTCurrentEntryIterator points to the editor timetable value (AnsiString OneLine) which includes all prestart comments, start time,
5614  comments within the timetable (start with '*') and valid timetable lines, but the TrainDataVector lists only valid
5615  timetable lines. So work through TimeTableEditVector (lists all OneLines) counting only valid tt lines, first being 0,
5616  and use that as the TrainDataVector position.
5617 
5618  Note that Sns-sh uses LinkedTrainEntryPtr to point to the feeder (in error) and its NonRepeatingShuttleLinkEntryPtr is NULL (again
5619  in error) but these links are not in fact used in the program, only those in Fns-sh are used and those are correct - Fns-sh uses
5620  LinkedTrainEntryPtr to point to the outward shuttle and NonRepeatingShuttleLinkEntryPtr to point to the finishing service.
5621  Even though Sns-sh doesn't link to the return shuttle its later Fns command does so all will be picked up.
5622  With these include in PreRepeatList all shuttles, but not non-repeating feeder and finshing services. Later on only expand services
5623  with repeats, and change commands as follows: F-nshs -> Fns, Snt-sh -> Snt, Sns-sh -> Sns, Fns-sh -> Fns, Frh-sh -> Frh and Sns-fsh -> Sns. */
5624 
5625  BreakFlag = false;
5626  BlankLineFound = false;
5627  StartTimeFound = false;
5628  TDEPtr = &TrainController->TrainDataVector.front();
5629  for(TTimetableEditVector::iterator TTEVIt = TimetableEditVector.begin(); TTEVIt != TimetableEditVector.end(); TTEVIt++)
5630  {
5631  while(!StartTimeFound && !TrainController->CheckTimeValidity(38, *TTEVIt, DummyTime))
5632  {
5633  BreakFlag = true;
5634  break;
5635  }
5636  if(BreakFlag)
5637  {
5638  BreakFlag = false;
5639  continue;
5640  }
5641  StartTimeFound = true;
5642  if(TrainController->CheckTimeValidity(39, *TTEVIt, DummyTime)) //step past start time
5643  {
5644  continue; //no subsequent entry within the timetable can start with a time or it wouldn't validate
5645  }
5646  if(*TTEVIt == "")
5647  {
5648  BlankLineFound = true; //if there is no blank line then the for... loop will terminate at last service or comment
5649  }
5650  if((!BlankLineFound && (*TTEVIt)[1] != '*'))
5651  { //valid service line
5652  if(TTEVIt == TTCurrentEntryIterator)
5653  {
5654  break;
5655  }
5656  TDEPtr++; //ready for next insert
5657  }
5658  }
5659 //add this service to the RepeatList then examine it, and add all linked references to the PreRepeatList
5660  RepeatList.push_back(TDEPtr);
5661  bool DuplicatePointer;
5662  for(TActionVectorIterator AVIt = TDEPtr->ActionVector.begin(); AVIt != TDEPtr->ActionVector.end(); AVIt++)
5663  {
5664  if((AVIt->Command == "Snt-sh") || (AVIt->Command == "Sns-sh") || (AVIt->Command == "Frh-sh") || (AVIt->Command == "Fns-sh") ||
5665  (AVIt->Command == "Sns") || (AVIt->Command == "Fns") || (AVIt->Command == "rsp") || (AVIt->Command == "fsp") ||
5666  (AVIt->Command == "Sfs") || (AVIt->Command == "jbo") || (AVIt->Command == "Fjo"))
5667  {
5668  DuplicatePointer = false;
5669  for(ListIt = RepeatList.begin(); ListIt != RepeatList.end(); ListIt++) //don't list any that have already been examined
5670  {
5671  if(*ListIt == AVIt->LinkedTrainEntryPtr)
5672  {
5673  DuplicatePointer = true;
5674  }
5675  }
5676  for(ListIt = PreRepeatList.begin(); ListIt != PreRepeatList.end(); ListIt++) //don't list any that have already been included
5677  {
5678  if(*ListIt == AVIt->LinkedTrainEntryPtr)
5679  {
5680  DuplicatePointer = true;
5681  }
5682  }
5683  if((!DuplicatePointer) && (AVIt->Command != "Sns-sh")) //Sns-sh has LinkedTrainEntryPtr (in error) as the feeder pointer
5684  {
5685  PreRepeatList.push_back(AVIt->LinkedTrainEntryPtr);
5686  }
5687  }
5688  }
5689 
5690  //now work through the PreRepeatList, adding new ones where find references and deleting when transfer to RepeatList
5691  while(!PreRepeatList.empty())
5692  {
5693  TTrainDataEntry* TDEPtr = PreRepeatList.front();
5694  PreRepeatList.pop_front();
5695  for(TActionVectorIterator AVIt = TDEPtr->ActionVector.begin(); AVIt != TDEPtr->ActionVector.end(); AVIt++)
5696  {
5697  if((AVIt->Command == "Snt-sh") || (AVIt->Command == "Sns-sh") || (AVIt->Command == "Frh-sh") || (AVIt->Command == "Fns-sh") ||
5698  (AVIt->Command == "Sns") || (AVIt->Command == "Fns") || (AVIt->Command == "rsp") || (AVIt->Command == "fsp") ||
5699  (AVIt->Command == "Sfs") || (AVIt->Command == "jbo") || (AVIt->Command == "Fjo"))
5700  {
5701  DuplicatePointer = false;
5702  for(ListIt = RepeatList.begin(); ListIt != RepeatList.end(); ListIt++) //don't list any that have already been examined
5703  {
5704  if(*ListIt == AVIt->LinkedTrainEntryPtr)
5705  {
5706  DuplicatePointer = true;
5707  }
5708  }
5709  for(ListIt = PreRepeatList.begin(); ListIt != PreRepeatList.end(); ListIt++) //don't list any that have already been included
5710  {
5711  if(*ListIt == AVIt->LinkedTrainEntryPtr)
5712  {
5713  DuplicatePointer = true;
5714  }
5715  }
5716  if((!DuplicatePointer) && (AVIt->Command != "Sns-sh")) //Sns-sh has LinkedTrainEntryPtr (in error) as the feeder pointer
5717  {
5718  PreRepeatList.push_back(AVIt->LinkedTrainEntryPtr);
5719  }
5720  }
5721  }
5722  RepeatList.push_back(TDEPtr);
5723  }
5724 
5725  //work through RepeatList making changes to the underlying timetable edit vector entries where have feeders (linked to Sns-sh entries) and
5726  //finishers (linked to Fns-sh entries)
5727  for(ListIt = RepeatList.begin(); ListIt != RepeatList.end(); ListIt++)
5728  {
5729  TTrainDataEntry* TDEPtr = *ListIt;
5730  for(TActionVectorIterator AVIt = TDEPtr->ActionVector.begin(); AVIt != TDEPtr->ActionVector.end(); AVIt++)
5731  {
5732  if(AVIt->Command == "Sns-sh")
5733  {
5734  if(LinkMap.find(AVIt->LinkedTrainEntryPtr) != LinkMap.end())
5735  {
5736  AnsiString* Feeder = LinkMap.find(AVIt->LinkedTrainEntryPtr)->second; //Sns-sh uses LinkedTrainEntryPtr (in error) for the feeder
5737  int FPos = Feeder->Pos("F-nshs");
5738  if(FPos > 0) //can't include an else here as commands may already have been changed
5739  {
5740  Feeder->Delete(FPos, 6);
5741  Feeder->Insert("Fns", FPos);
5742  }
5743  }
5744  }
5745  else if(AVIt->Command == "Fns-sh")
5746  {
5747  if(LinkMap.find(AVIt->NonRepeatingShuttleLinkEntryPtr) != LinkMap.end())
5748  {
5749  AnsiString* Finisher = LinkMap.find(AVIt->NonRepeatingShuttleLinkEntryPtr)->second;
5750  int SPos = Finisher->Pos("Sns-fsh");
5751  if(SPos > 0) //can't include an else here as commands may already have been changed
5752  {
5753  //first change the source reference after Sns-sh by (Digits * Repeats) as that will have changed when expanded
5754  int TempPos = SPos + 8; //now on 1st character of the source reference
5755  AnsiString SourceRef = "";
5756  while((*Finisher)[TempPos] != ',')
5757  {
5758  SourceRef += (*Finisher)[TempPos];
5759  TempPos++;
5760  }
5761  AddRefDigits(SourceRef, SPos + 8, *Finisher, (Digits * Repeats));
5762  Finisher->Delete(SPos + 3, 4); //drop the '-fsh'
5763  }
5764  }
5765  }
5766  }
5767  }
5768 
5769  //here when have all linked repeating services in RepeatList, add marker "####" to start of all entries to be expanded. With this it doesn't
5770  //matter what order the RepeatList service pointers are in, as expansion will be done in the original timetable order when the markers are identified
5771 
5772  AnsiString* TrainEditEntryPtr;
5773  TimetableChangedFlag = true;
5774  while(!RepeatList.empty())
5775  {
5776  TTrainDataEntry* TDEPtr = RepeatList.front();
5777  if(LinkMap.find(TDEPtr) != LinkMap.end())
5778  {
5779  TrainEditEntryPtr = LinkMap.find(TDEPtr)->second;
5780  }
5781  else //failed to find TDEPtr
5782  {
5783  ShowMessage("Failed to find an entry link, internal timetable has probably been corrupted, please reload the original timetable");
5784  Utilities->CallLogPop(2634);
5785  return;
5786  }
5787  RepeatList.pop_front();
5788 
5789  RpIndex = TrainEditEntryPtr->Pos(",R;"); //RpIndex is the position of the comma
5790  if(RpIndex > 0)
5791  {
5792  TrainEditEntryPtr->Delete(RpIndex, (TrainEditEntryPtr->Length() - RpIndex + 1)); //delete the repeat command before adding the marker
5793  *TrainEditEntryPtr = "####" + *TrainEditEntryPtr; //so there's room for the marker without the risk of
5794  } //the vector being reallocated
5795  else
5796  {
5797  ShowMessage("Failed to find a repeat entry, internal timetable has probably been corrupted, please reload the original timetable");
5798  Utilities->CallLogPop(2635);
5799  return;
5800  }
5801  }
5802 
5803 //NB: from here don't rely on LinkMap or TimetableEditVector iterators or pointers as they will likely change when services expand
5804 
5805  //now expand each entry with a marker
5806  int IteratorCount = 0; //use this instead of an explicit iterator because vector likely to be reallocated during insertions
5807  AnsiString OutwardShuttleRef = "", ReturnShuttleRef = "", FeederRef = "", FinishRef = "";
5808  int OutwardShuttlePos = 0, ReturnShuttlePos = 0, FinishPos = 0;
5809  bool FirstPass = true;
5810  int FirstEntryIterator;
5811  while(true)
5812  {
5813  if((TimetableEditVector.begin() + IteratorCount) == TimetableEditVector.end())
5814  {
5815  break;
5816  }
5817  if((TimetableEditVector.begin() + IteratorCount)->SubString(1,4) == "####")
5818  {
5819  if(FirstPass)
5820  {
5821  FirstEntryIterator = IteratorCount; //can use this to find TTCurrentEntryIterator as nothing changes before this
5822  FirstPass = false;
5823  }
5824  AnsiString EntryCopy = *(TimetableEditVector.begin() + IteratorCount);
5825  EntryCopy.Delete(1, 4); //remove the marker
5826  int Sns_shPos = EntryCopy.Pos("Sns-sh");
5827  int Fns_shPos = EntryCopy.Pos("Fns-sh");
5828  int Snt_shPos = EntryCopy.Pos("Snt-sh");
5829  int Frh_shPos = EntryCopy.Pos("Frh-sh");
5830  if(Sns_shPos > 0)
5831  {
5832  /* feeder 1X00 -> outward 1A00 -> return 1A01 -> outward 1st repeat 1A02 -> return 1st repeat 1A03 -> ...
5833  Shuttle syntax: '... 1A00;description,07:00;Sns-sh;1A01;1X00, ...'
5834  New syntax for original entry: 1A00;description,07:00;Sns;1X00, ...'
5835  New syntax for first repeat entry: 1A02;description,07:00;Sns;1A01, ...' i.e original return shuttle ref
5836  New syntax for nth repeat entry: 1A00+Digits;description,07:00;Sns;1A99+Digits, ...'
5837  */
5838  //set return shuttle ref
5839  int TempPos = Sns_shPos + 7; //TempPos is at the position after the ';' following 'Sns-sh', i.e. at start of ReturnShuttleRef
5840  ReturnShuttlePos = TempPos;
5841  while(EntryCopy[TempPos] != ';')
5842  {
5843  ReturnShuttleRef += EntryCopy[TempPos];
5844  TempPos++; //when exits TempPos at the position of the ';' following ReturnShuttleRef
5845  }
5846  TempPos++; //now at start of FeederRef
5847  while(EntryCopy[TempPos] != ',')
5848  {
5849  FeederRef += EntryCopy[TempPos];
5850  TempPos++; //when exits TempPos at the position of the ',' following FeederRef
5851  }
5852  //now change EntryCopy as necessary prior to expansion, but do deletions from end to start so Pos values stay valid
5853  EntryCopy.Delete(ReturnShuttlePos, ReturnShuttleRef.Length() + 1); //+1 to delete the ';'
5854  EntryCopy.Delete(Sns_shPos + 3, 3); //drop the '-sh'
5855  //copy it back to the vector
5856  *(TimetableEditVector.begin() + IteratorCount) = EntryCopy;
5857  IteratorCount++;
5858  //now deal with the repeats, but for these need ReturnShuttleRef rather than FeederRef, so alter that first
5859  EntryCopy.Delete(ReturnShuttlePos - 3, FeederRef.Length()); //-3 because have removed '-sh' and keep the ';'
5860  EntryCopy.Insert(ReturnShuttleRef, ReturnShuttlePos - 3); //now ready for the repeat insertions
5861  for(int x = 1; x <= Repeats; x++)
5862  {
5863  for(int Position = 1; Position <= EntryCopy.Length(); Position++)
5864  {
5865  bool FoundRef = false;
5866  AnsiString AnsiServRef = "";
5867  if(Position == 1) //for this service ref
5868  {
5869  FoundRef = true;
5870  }
5871  else if(Position == ReturnShuttlePos - 3) //return shuttle ref
5872  {
5873  FoundRef = true;
5874  }
5875  else if((EntryCopy.SubString(Position, 3) == "Fns") || (EntryCopy.SubString(Position, 3) == "rsp") ||
5876  (EntryCopy.SubString(Position, 3) == "fsp") || (EntryCopy.SubString(Position, 3) == "Fjo") ||
5877  (EntryCopy.SubString(Position, 3) == "jbo"))
5878  {
5879  Position += 4; //any other referenced services
5880  FoundRef = true;
5881  }
5882  if(FoundRef)
5883  {
5884  int y = Position;
5885  while((y <= EntryCopy.Length()) && (EntryCopy[y] != ';') && (EntryCopy[y] != ','))
5886  {
5887  AnsiServRef += EntryCopy[y];
5888  y++;
5889  }
5890  if((x > 1) || Position != ReturnShuttlePos - 3)//1st repeat needs to keep the original return shuttle ref after the 'Sns'
5891  {
5892  AddRefDigits(AnsiServRef, Position, EntryCopy, Digits);
5893  }
5894  }
5895  }
5896  //change all time strings in EntryCopy by Mins
5897  for(int z = 1; z <= EntryCopy.Length(); z++)
5898  {
5899  if(TrainController->CheckTimeValidity(40, EntryCopy.SubString(z,5), DummyTime)) //text after time ignored
5900  {
5901  AnsiString NewTime = AddTimeMinutes(EntryCopy.SubString(z,5), Mins);
5902  EntryCopy.Delete(z, 5);
5903  EntryCopy.Insert(NewTime, z); //this ok as doesn't alter overall length of EntryCopy
5904  }
5905  }
5906  //insert the entry
5907  TimetableEditVector.insert(TimetableEditVector.begin() + IteratorCount, EntryCopy);
5908  IteratorCount++;
5909  }
5910  continue;
5911  }
5912  else if(Snt_shPos > 0)
5913  {
5914  /* outward 1A00 -> return 1A01 -> outward 1st repeat 1A02 -> return 1st repeat 1A03 -> ...
5915  Shuttle syntax: '... 1A00;description;train data,07:00;Snt-sh;15-40 15-41;1A01, ...'
5916  New syntax for original entry: 1A00;description,07:00;Snt;15-40 15-41, ...' (train data, '-sh' & return ref dropped)
5917  New syntax for first repeat entry: 1A02;description,07:00;Sns;1A01, ...' i.e original return shuttle ref
5918  New syntax for nth repeat entry: 1A00+n x Digits;description,07:00;Sns;1A99+n x Digits, ...'
5919  */
5920  //set return shuttle ref
5921  AnsiString StartLoc = "";
5922  int TempPos = Snt_shPos + 7; //TempPos is at the position after the ';' following 'Snt-sh', i.e. at start of StartLoc
5923  int StartLocPos = TempPos;
5924  while(EntryCopy[TempPos] != ';')
5925  {
5926  StartLoc += EntryCopy[TempPos];
5927  TempPos++; //when exits TempPos at the position of the ';' following the start location
5928  }
5929  TempPos++; //now at start of Return shuttle
5930  ReturnShuttlePos = TempPos;
5931  while(EntryCopy[TempPos] != ',')
5932  {
5933  ReturnShuttleRef += EntryCopy[TempPos];
5934  TempPos++; //when exits TempPos at the position of the ',' following FeederRef
5935  }
5936  //now change EntryCopy as necessary prior to expansion, but do deletions from end to start so Pos values stay valid
5937  EntryCopy.Delete(ReturnShuttlePos, ReturnShuttleRef.Length()); //leave the comma
5938  EntryCopy.Delete(StartLocPos, StartLoc.Length() + 1); //+1 to delete the ';'
5939  EntryCopy.Delete(Snt_shPos + 3, 3); //drop the '-sh'
5940  EntryCopy.Insert(StartLoc, StartLocPos - 3); //-3 as dropped '-sh'
5941  //copy it back to the vector
5942  *(TimetableEditVector.begin() + IteratorCount) = EntryCopy;
5943  IteratorCount++;
5944  //now deal with the repeats, but for these need ReturnShuttleRef rather than starting location and 'Sns' rather than 'Snt'
5945  //& change ReturnRefPos to the StartLocPos - 3
5946  ReturnShuttlePos = StartLocPos - 3;
5947  AnsiString TrainDataString = "";
5948  int TrainDataPos;
5949  for(int x = 1; x <= Repeats; x++)
5950  {
5951  if(x == 1)
5952  {
5953  EntryCopy.Delete(StartLocPos - 3, StartLoc.Length()); //drop StartLoc
5954  EntryCopy.Insert(ReturnShuttleRef, StartLocPos - 3); //replace with ReturnShuttleRef
5955  EntryCopy.Delete(Snt_shPos, 3); //drop 'Snt'
5956  EntryCopy.Insert("Sns", Snt_shPos); //replace with 'Sns'
5957  //now drop train data, find TrainDataPos after 2nd ';' & delete up to the ',' but keep the comma
5958  TempPos = 1;
5959  while(EntryCopy[TempPos] != ';')
5960  {
5961  TempPos++; //when exits TempPos at the position of the ';' following the service ref
5962  }
5963  TempPos++; //now at start of description
5964  while(EntryCopy[TempPos] != ';')
5965  {
5966  TempPos++; //when exits TempPos at the position of the ';' following the description
5967  }
5968  TempPos++; //now at start of train data
5969  TrainDataPos = TempPos;
5970  while(EntryCopy[TempPos] != ',')
5971  {
5972  TrainDataString += EntryCopy[TempPos];
5973  TempPos++; //when exits TempPos at the position of the ',' following the train data
5974  }
5975  TrainDataPos--; //now at ';' after description
5976  EntryCopy.Delete(TrainDataPos, TrainDataString.Length() + 1); //delete the data & ';' after description but keep the comma
5977  }
5978  for(int Position = 1; Position <= EntryCopy.Length(); Position++)
5979  {
5980  bool FoundRef = false;
5981  AnsiString AnsiServRef = "";
5982  if(Position == 1) //for this service ref
5983  {
5984  FoundRef = true;
5985  }
5986  else if(Position == EntryCopy.Pos("Sns") + 4)
5987  {
5988  ReturnShuttlePos = Position;
5989  FoundRef = true;
5990  }
5991  else if((EntryCopy.SubString(Position, 3) == "Fns") || (EntryCopy.SubString(Position, 3) == "rsp") ||
5992  (EntryCopy.SubString(Position, 3) == "fsp") || (EntryCopy.SubString(Position, 3) == "Fjo") ||
5993  (EntryCopy.SubString(Position, 3) == "jbo"))
5994  {
5995  Position += 4; //any other referenced services
5996  FoundRef = true;
5997  }
5998  if(FoundRef)
5999  {
6000  int y = Position;
6001  while((y <= EntryCopy.Length()) && (EntryCopy[y] != ';') && (EntryCopy[y] != ','))
6002  {
6003  AnsiServRef += EntryCopy[y];
6004  y++;
6005  }
6006  if((x > 1) || Position != ReturnShuttlePos)//1st repeat needs to keep the original return shuttle ref after the 'Sns'
6007  {
6008  AddRefDigits(AnsiServRef, Position, EntryCopy, Digits);
6009  }
6010  }
6011  }
6012  //change all time strings in EntryCopy by Mins
6013  for(int z = 1; z <= EntryCopy.Length(); z++)
6014  {
6015  if(TrainController->CheckTimeValidity(41, EntryCopy.SubString(z,5), DummyTime)) //text after time ignored
6016  {
6017  AnsiString NewTime = AddTimeMinutes(EntryCopy.SubString(z,5), Mins);
6018  EntryCopy.Delete(z, 5);
6019  EntryCopy.Insert(NewTime, z); //this ok as doesn't alter overall length of EntryCopy
6020  }
6021  }
6022  //insert the entry
6023  TimetableEditVector.insert(TimetableEditVector.begin() + IteratorCount, EntryCopy);
6024  IteratorCount++;
6025  }
6026  continue;
6027  }
6028  else if(Fns_shPos > 0) //has a finisher, syntax is '... ,HH:MM;Fns-sh;OutwardShuttleRef;FinishRef NULL or end of file
6029  {
6030  /* outward 1A00 -> return 1A01 -> outward 1st repeat 1A02 -> return 1st repeat 1A03 -> ... -> ... -> return last repeat 1A11; -> finisher 1X01
6031  Shuttle syntax: '... Fns-sh;1A00;1X01'
6032  New syntax for original entry: '... Fns;1A02' ('-sh' & finisher dropped)
6033  New syntax for first repeat entry: '... Fns;1A04'
6034  New syntax for last repeat entry: '... Fns;1X01'
6035  */
6036  //set outward shuttle ref
6037  int TempPos = Fns_shPos + 7; //TempPos is at the position after the ';' following 'Fns-sh', i.e. at start of OutwardShuttleRef
6038  OutwardShuttlePos = TempPos;
6039  while(EntryCopy[TempPos] != ';')
6040  {
6041  OutwardShuttleRef += EntryCopy[TempPos];
6042  TempPos++; //when exits TempPos at the position of the ';' following the OutwardShuttleRef
6043  }
6044  TempPos++; //now at start of FinishRef
6045  FinishPos = TempPos;
6046  while(TempPos <= EntryCopy.Length()) //FinishRef is the last element in the entry apart from a final NULL
6047  {
6048  FinishRef += EntryCopy[TempPos];
6049  TempPos++; //when exits TempPos at the position of the NULL following FinishRef
6050  }
6051  //now change EntryCopy as necessary prior to expansion, but do deletions from end to start so Pos values stay valid
6052  EntryCopy.Delete(FinishPos, FinishRef.Length() + 1); //delete the finish ref and the ';' as all but last repeat refer back to the outward shuttle
6053  EntryCopy.Delete(Fns_shPos + 3, 3); //drop the '-sh' //this is now the end of the entry
6054 
6055  //BUT, need to link back to the next repeat of the outward shuttle ref, so add the digits to OutwardShuttleRef and replace the original
6056  AddRefDigits(OutwardShuttleRef, OutwardShuttlePos - 3, EntryCopy, Digits); //-3 because have removed '-sh'
6057  //copy it back to the vector
6058  *(TimetableEditVector.begin() + IteratorCount) = EntryCopy;
6059  IteratorCount++;
6060  //now deal with the repeats
6061  for(int x = 1; x <= Repeats; x++)
6062  {
6063  if(x == Repeats) //at last repeat need the finishing ref
6064  {
6065  EntryCopy.Delete(OutwardShuttlePos - 3, OutwardShuttleRef.Length()); //-3 because have removed '-sh' and keep the ';'
6066  EntryCopy.Insert(FinishRef, OutwardShuttlePos - 3);
6067  }
6068  for(int Position = 1; Position <= EntryCopy.Length(); Position++)
6069  {
6070  bool FoundRef = false;
6071  AnsiString AnsiServRef = "";
6072  if(Position == 1) //for this service ref
6073  {
6074  FoundRef = true;
6075  }
6076  else if(Position == OutwardShuttlePos - 3) //outward shuttle ref
6077  {
6078  FoundRef = true;
6079  }
6080  else if((EntryCopy.SubString(Position, 3) == "Sns") || (EntryCopy.SubString(Position, 3) == "rsp") ||
6081  (EntryCopy.SubString(Position, 3) == "fsp") || (EntryCopy.SubString(Position, 3) == "Fjo") ||
6082  (EntryCopy.SubString(Position, 3) == "jbo"))
6083  {
6084  Position += 4; //any other referenced services
6085  FoundRef = true;
6086  }
6087  if(FoundRef)
6088  {
6089  int y = Position;
6090  while((y <= EntryCopy.Length()) && (EntryCopy[y] != ';') && (EntryCopy[y] != ','))
6091  {
6092  AnsiServRef += EntryCopy[y];
6093  y++;
6094  }
6095  if((x < Repeats) || (AnsiServRef != FinishRef)) //don't change the finish reference
6096  {
6097  AddRefDigits(AnsiServRef, Position, EntryCopy, Digits);
6098  }
6099  }
6100  }
6101  //change all time strings in EntryCopy by Mins
6102  for(int z = 1; z <= EntryCopy.Length(); z++)
6103  {
6104  if(TrainController->CheckTimeValidity(42, EntryCopy.SubString(z,5), DummyTime)) //text after time ignored
6105  {
6106  AnsiString NewTime = AddTimeMinutes(EntryCopy.SubString(z,5), Mins);
6107  EntryCopy.Delete(z, 5);
6108  EntryCopy.Insert(NewTime, z); //this ok as doesn't alter overall length of EntryCopy
6109  }
6110  }
6111  //insert the entry
6112  TimetableEditVector.insert(TimetableEditVector.begin() + IteratorCount, EntryCopy);
6113  IteratorCount++;
6114  }
6115  continue;
6116  }
6117  else if(Frh_shPos > 0) //only the last repeat becomes Frh, others to be Fns followed by next repeat of the shuttle
6118  {
6119  /* outward 1A00 -> return 1A01 -> outward 1st repeat 1A02 -> return 1st repeat 1A03 -> ... -> ... -> return last repeat 1A11; final command 'Frh'
6120  Shuttle syntax: '... Frh-sh;1A00'
6121  New syntax for original entry: '... Fns;1A02' ('-sh' dropped)
6122  New syntax for first repeat entry: '... Fns;1A04'
6123  New syntax for last repeat entry: '... Frh' (time dropped)
6124  */
6125  //set outward shuttle ref
6126  int TempPos = Frh_shPos + 7; //TempPos is at the position after the ';' following 'Frh-sh', i.e. at start of OutwardShuttleRef
6127  OutwardShuttlePos = TempPos;
6128  while(TempPos <= EntryCopy.Length())
6129  {
6130  OutwardShuttleRef += EntryCopy[TempPos];
6131  TempPos++; //when exits TempPos after end of EntryCopy
6132  }
6133  //now change EntryCopy as necessary prior to expansion, but do deletions from end to start so Pos values stay valid
6134  EntryCopy.Delete(Frh_shPos, 6); //delete Frh-sh
6135  EntryCopy.Insert("Fns", Frh_shPos); //insert Fns & leave outward shuttle ref in place
6136 
6137  //BUT, need to link back to the next repeat of the outward shuttle ref, so add the digits to OutwardShuttleRef and replace the original
6138  AddRefDigits(OutwardShuttleRef, OutwardShuttlePos - 3, EntryCopy, Digits); //-3 because have removed 'Frh-sh' and replaced with 'Fns'
6139  //copy it back to the vector
6140  *(TimetableEditVector.begin() + IteratorCount) = EntryCopy;
6141  IteratorCount++;
6142  //now deal with the repeats
6143  for(int x = 1; x <= Repeats; x++)
6144  {
6145  if(x == Repeats) //at last repeat need Frh with no time and no service ref
6146  {
6147  EntryCopy.Delete(Frh_shPos - 6, EntryCopy.Length() - Frh_shPos + 7); //convert to Frh & get rid of the shuttle link and the time
6148  EntryCopy.Insert("Frh", Frh_shPos - 6);
6149  }
6150  for(int Position = 1; Position <= EntryCopy.Length(); Position++)
6151  {
6152  bool FoundRef = false;
6153  AnsiString AnsiServRef = "";
6154  if(Position == 1) //for this service ref
6155  {
6156  FoundRef = true;
6157  }
6158  else if(Position == OutwardShuttlePos - 3) //outward shuttle ref
6159  {
6160  FoundRef = true;
6161  }
6162  else if((EntryCopy.SubString(Position, 3) == "Sns") || (EntryCopy.SubString(Position, 3) == "rsp") ||
6163  (EntryCopy.SubString(Position, 3) == "fsp") || (EntryCopy.SubString(Position, 3) == "Fjo") ||
6164  (EntryCopy.SubString(Position, 3) == "jbo"))
6165  {
6166  Position += 4; //any other referenced services
6167  FoundRef = true;
6168  }
6169  if(FoundRef)
6170  {
6171  int y = Position;
6172  while((y <= EntryCopy.Length()) && (EntryCopy[y] != ';') && (EntryCopy[y] != ','))
6173  {
6174  AnsiServRef += EntryCopy[y];
6175  y++;
6176  }
6177  if((x < Repeats) || (AnsiServRef != FinishRef)) //don't change the finish reference
6178  {
6179  AddRefDigits(AnsiServRef, Position, EntryCopy, Digits);
6180  }
6181  }
6182  }
6183  //change all time strings in EntryCopy by Mins
6184  for(int z = 1; z <= EntryCopy.Length(); z++)
6185  {
6186  if(TrainController->CheckTimeValidity(43, EntryCopy.SubString(z,5), DummyTime)) //text after time ignored
6187  {
6188  AnsiString NewTime = AddTimeMinutes(EntryCopy.SubString(z,5), Mins);
6189  EntryCopy.Delete(z, 5);
6190  EntryCopy.Insert(NewTime, z); //this ok as doesn't alter overall length of EntryCopy
6191  }
6192  }
6193  //insert the entry
6194  TimetableEditVector.insert(TimetableEditVector.begin() + IteratorCount, EntryCopy);
6195  IteratorCount++;
6196  }
6197  continue;
6198  }
6199  //all remainder have only 3 letter commands, just copy it back to the vector (without the '####' & deal with repeats
6200  *(TimetableEditVector.begin() + IteratorCount) = EntryCopy;
6201  IteratorCount++;
6202  //now deal with the repeats
6203  for(int x = 1; x <= Repeats; x++)
6204  {
6205  for(int Position = 1; Position <= EntryCopy.Length(); Position++)
6206  {
6207  bool FoundRef = false;
6208  AnsiString AnsiServRef = "";
6209  if(Position == 1) //for this service ref
6210  {
6211  FoundRef = true;
6212  }
6213  else if((EntryCopy.SubString(Position - 1, 1) == ";") && (EntryCopy.SubString(Position + 3, 1) == ";") && //added ";" condits at v2.20.2 in response
6214  ((EntryCopy.SubString(Position, 3) == "Sns") || (EntryCopy.SubString(Position, 3) == "Fns") || //to error reported by Shotty_Gunny on 9/9/24
6215  (EntryCopy.SubString(Position, 3) == "rsp") || (EntryCopy.SubString(Position, 3) == "fsp") || //via Discord (ticket #53)
6216  (EntryCopy.SubString(Position, 3) == "Sfs") || (EntryCopy.SubString(Position, 3) == "Fjo") ||
6217  (EntryCopy.SubString(Position, 3) == "jbo")))
6218  {
6219  Position += 4; //any other referenced services
6220  FoundRef = true;
6221  }
6222  if(FoundRef)
6223  {
6224  int y = Position;
6225  while((y <= EntryCopy.Length()) && (EntryCopy[y] != ';') && (EntryCopy[y] != ','))
6226  {
6227  AnsiServRef += EntryCopy[y];
6228  y++;
6229  }
6230  AddRefDigits(AnsiServRef, Position, EntryCopy, Digits);
6231  }
6232  }
6233  //change all time strings in EntryCopy by Mins
6234  for(int z = 1; z <= EntryCopy.Length(); z++)
6235  {
6236  if(TrainController->CheckTimeValidity(44, EntryCopy.SubString(z,5), DummyTime)) //text after time ignored
6237  {
6238  AnsiString NewTime = AddTimeMinutes(EntryCopy.SubString(z,5), Mins);
6239  EntryCopy.Delete(z, 5);
6240  EntryCopy.Insert(NewTime, z); //this ok as doesn't alter overall length of EntryCopy
6241  }
6242  }
6243  //insert the entry
6244  TimetableEditVector.insert(TimetableEditVector.begin() + IteratorCount, EntryCopy);
6245  IteratorCount++;
6246  }
6247  }
6248  else
6249  {
6250  IteratorCount++;
6251  if((TimetableEditVector.begin() + IteratorCount) == TimetableEditVector.end())
6252  {
6253  break;
6254  }
6255  }
6256  }
6258  //reset TTCurrentEntryIterator
6259  TTCurrentEntryIterator = TimetableEditVector.begin() + FirstEntryIterator;
6261  SetLevel1Mode(140);
6262  Utilities->CallLogPop(2636);
6263  }
6264  catch(const Exception &e) //non-error catch
6265  {
6266  ShowMessage("The following error occurred during expansion: " + e.Message + ". Please discard the modified timetable"
6267  " which is likely to be corrupt and reload the original.");
6268  Utilities->CallLogPop(2637);
6269  }
6270 }
6271 
6272 //---------------------------------------------------------------------------
6273 
6274 AnsiString TInterface::AddTimeMinutes(AnsiString OrigTimeString, int MinsToAdd) //added at v2.17.0
6275 {//only used in ExpandRepeatsButtonClick function, errors caught in that function
6276  TrainController->LogEvent("AddTimeMinutes");
6277  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddTimeMinutes");
6278  int OrigMins = OrigTimeString.SubString(4,2).ToInt();
6279  int Hrs = OrigTimeString.SubString(1,2).ToInt();
6280  OrigMins += MinsToAdd;
6281  while(OrigMins >= 60)
6282  {
6283  OrigMins -= 60;
6284  Hrs++;
6285  }
6286  AnsiString MinsStr = AnsiString(OrigMins), HrsStr = AnsiString(Hrs);
6287  if(OrigMins < 10)
6288  {
6289  MinsStr = "0" + MinsStr;
6290  }
6291  if(Hrs < 10)
6292  {
6293  HrsStr = "0" + HrsStr;
6294  }
6295  Utilities->CallLogPop(2638);
6296  return(HrsStr + ":" + MinsStr);
6297 }
6298 
6299 //---------------------------------------------------------------------------
6300 
6301 void TInterface::AddRefDigits(AnsiString AnsiServRef, int Position, AnsiString &EntryCopy, int Digits) //added at v2.17.0
6302 {//only used in ExpandRepeatsButtonClick function, errors caught in that function
6303  TrainController->LogEvent("AddRefDigits");
6304  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddRefDigits");
6305  if(Digits > 0) //if Digits == 0 then don't change anything, if Digits > 0 but no digits in last 2 headcode places then wouldn't have validated //condition added at v2.20.2
6306  {
6307  AnsiString AnsiLastDigits = AnsiServRef.SubString(AnsiServRef.Length() - 1, 2);
6308  int LastDigits = AnsiLastDigits.ToInt();
6309  int NewDigits = (LastDigits + Digits) % 100;
6310  AnsiString AnsiNewDigits = AnsiString(NewDigits);
6311  if(NewDigits < 10)
6312  {
6313  AnsiNewDigits = "0" + AnsiNewDigits;
6314  }
6315  EntryCopy = EntryCopy.Delete(Position + AnsiServRef.Length() - 2, 2);
6316  EntryCopy = EntryCopy.Insert(AnsiNewDigits, Position + AnsiServRef.Length() - 2);
6317  }
6318  Utilities->CallLogPop(2639);
6319 }
6320 
6321 //---------------------------------------------------------------------------
6322 
6323 void __fastcall TInterface::LocationNameComboBoxClick(TObject *Sender)
6324 {
6325  try
6326  {
6327  TrainController->LogEvent("LocationNameComboBoxClick");
6328  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LocationNameComboBoxClick");
6330  {
6331  LocationNameComboBox->SelectAll();
6332  int SelPos = OneEntryTimetableMemo->SelStart;
6333  AnsiString FirstPart = OneEntryTimetableMemo->Text.SubString(1, SelPos);
6334  AnsiString LastPart = OneEntryTimetableMemo->Text.SubString(SelPos + 1, OneEntryTimetableMemo->Text.Length() - SelPos);
6335  OneEntryTimetableMemo->Text = FirstPart + LocationNameComboBox->SelText + LastPart;
6336  OneEntryTimetableMemo->SelStart = SelPos + LocationNameComboBox->SelText.Length();
6337  TTEntryChangedFlag = true;
6338  OneEntryTimetableMemo->SetFocus();
6340  SetLevel1Mode(118);
6341  }
6342  Utilities->CallLogPop(1669);
6343  }
6344  catch(const Exception &e)
6345  {
6346  ErrorLog(109, e.Message);
6347  }
6348 }
6349 
6350 // ---------------------------------------------------------------------------
6351 void __fastcall TInterface::OneEntryTimetableMemoKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
6352 {
6353  try
6354  {
6355 // TrainController->LogEvent("OneEntryTimetableMemoKeyUp"); drop this - too many entries
6356  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",OneEntryTimetableMemoKeyUp");
6358  {
6359  Utilities->CallLogPop(1716);
6360  return;
6361  }
6362  TimetableChangedFlag = true;
6363  TTEntryChangedFlag = true;
6364  TimetableValidFlag = false;
6366  SetLevel1Mode(127);
6367  Utilities->CallLogPop(1629);
6368  }
6369  catch(const Exception &e)
6370  {
6371  ErrorLog(66, e.Message);
6372  }
6373 }
6374 
6375 // ---------------------------------------------------------------------------
6376 void __fastcall TInterface::AddSubMinsBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
6377 {
6378 // forces a recheck for whether addmins/submins buttons should be enabled
6379  try
6380  {
6381  TrainController->LogEvent("AddSubMinsBoxKeyUp");
6382  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AddSubMinsBoxKeyUp");
6384  SetLevel1Mode(108);
6385  Utilities->CallLogPop(1658);
6386  }
6387  catch(const Exception &e)
6388  {
6389  ErrorLog(106, e.Message);
6390  }
6391 }
6392 
6393 // ---------------------------------------------------------------------------
6394 void __fastcall TInterface::LocationNameComboBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
6395 {
6396  try
6397  {
6398  TrainController->LogEvent("LocationNameComboBoxKeyUp");
6399  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LocationNameComboBoxKeyUp");
6400  if(!Track->LocationNameMultiMap.empty())
6401  {
6402  LocationNameComboBox->Text = "Location names";
6403  }
6404  else
6405  {
6406  LocationNameComboBox->Text = "No locations (listed when a railway with names is loaded)";
6407  }
6408  Utilities->CallLogPop(1677);
6409  }
6410  catch(const Exception &e)
6411  {
6412  ErrorLog(112, e.Message);
6413  }
6414 }
6415 
6416 // ---------------------------------------------------------------------------
6417 
6418 void __fastcall TInterface::AllEntriesTTListBoxMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
6419 {
6420 // Select the item pointed to unless a 'save entry' is pending in which case ignore
6421  try
6422  {
6423  TrainController->LogEvent("AllEntriesTTListBoxMouseUp," + AnsiString(X) + "," + AnsiString(Y));
6424  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AllEntriesTTListBoxMouseUp," + AnsiString(X) + "," + AnsiString(Y));
6426  {
6427  Utilities->CallLogPop(1687);
6428  return;
6429  }
6430  if(TTEntryChangedFlag || NewEntryInPreparationFlag) // if a save/cancel pending don't permit anything else
6431  {
6432  Utilities->CallLogPop(1688);
6433  return;
6434  }
6435  int TopPos = AllEntriesTTListBox->TopIndex; // need to store this & reset it after SetLevel1Mode to prevent the scroll
6436  // position changing in AllEntriesTTListBox
6437 
6438 //substituted for the below
6439  int LBIndex = AllEntriesTTListBox->ItemAtPos(TPoint(X,Y), true);
6440  if(LBIndex == -1)
6441  {
6443  }
6444  else
6445  {
6446  TTCurrentEntryIterator = TimetableEditVector.begin() + LBIndex;
6447  }
6448 //dropped at v2.13.0 as unreliable on high resolution monitors
6449  // find item required - 13 pixels per line of text
6450 /* if((TopPos + (Y / 13)) >= AllEntriesTTListBox->Items->Count)
6451  {
6452  TTCurrentEntryIterator = TimetableEditVector.end() - 1;
6453  }
6454  else
6455  {
6456  TTCurrentEntryIterator = TimetableEditVector.begin() + (Y / 13) + TopPos;
6457  }
6458 */
6459  int OldVectorPos = TTCurrentEntryIterator - TimetableEditVector.begin(); // save the old position
6461 // reset the TTCurrentEntryIterator after CompileAllEntriesMemoAndSetIterators
6462  if(OldVectorPos >= TimetableEditVector.end() - TimetableEditVector.begin() - 1)
6463  {
6465  }
6466  else
6467  {
6468  TTCurrentEntryIterator = TimetableEditVector.begin() + OldVectorPos;
6469  }
6471  SetLevel1Mode(120);
6472  AllEntriesTTListBox->TopIndex = TopPos; // reset it after SetLevel1Mode to prevent the scroll position changing
6473  Utilities->CallLogPop(1648);
6474  }
6475  catch(const Exception &e)
6476  {
6477  ErrorLog(103, e.Message);
6478  }
6479 }
6480 
6481 // ---------------------------------------------------------------------------
6482 
6484 {
6485  enum
6486  {
6487  PreStartTime, ActiveSegment, PostEnd
6488  } Segment;
6489  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CompileAllEntriesMemoAndSetIterators");
6490  AllEntriesTTListBox->Clear();
6491  TEVIterator = TimetableEditVector.end(); //these are iterators so can't use '0'in 64bit version, initialise to an invalid location to force errors if not set properly
6495  if(TimetableEditVector.empty())
6496  {
6498  Utilities->CallLogPop(1681);
6499  return;
6500  }
6501  Segment = PreStartTime;
6503  {
6504  if(Segment == PreStartTime) // looking for the start time
6505  {
6506  TDateTime TempTime; // dummy
6507  if(TrainController->CheckTimeValidity(33, *TEVIterator, TempTime))
6508  {
6509  TTStartTimeIterator = TEVIterator; // TTStartTimeBox text set in TTHandler
6510  AllEntriesTTListBox->Items->Add("START " + (*TEVIterator).SubString(1, 5));
6511  Segment = ActiveSegment;
6512  continue;
6513  }
6514  else
6515  {
6516  if(*TEVIterator == "")
6517  {
6518  AllEntriesTTListBox->Items->Add("- Blank");
6519  }
6520  else
6521  {
6522  AnsiString CurrentStr = *TEVIterator;
6523  if(CurrentStr != "") // strip any non alphanumeric characters (specifically \r or \n)
6524  {
6525  CurrentStr = CurrentStr.SubString(1, 10); // limit length for LH window
6526  for(int x = 1; x < CurrentStr.Length(); x++)
6527  {
6528  if((CurrentStr[x] < 32) || (CurrentStr[1] > 126))
6529  {
6530  CurrentStr = CurrentStr.SubString(1, (x - 1));
6531  }
6532  }
6533  }
6534  AllEntriesTTListBox->Items->Add("- " + CurrentStr);
6535  }
6536  continue;
6537  }
6538  }
6539  if(Segment == ActiveSegment)
6540  {
6541  if(*TEVIterator != "")
6542  {
6543  if((*TEVIterator)[1] != '*')
6544  {
6546  ConvertCRLFsToCommas(0, *TEVIterator); // This needed because an entry intended as a service might have skipped the conversion in
6547  // SaveTTEntryButtonClick - see comment in that function
6549  {
6551  }
6553  }
6554  AnsiString Entry = *TEVIterator;
6555 
6556 /* dropped at v2.9.1 so comment entries more meaningful, they self truncate to AllEntriesTTListBox width
6557  if(Entry[1] == '*')
6558  {
6559  Entry = "Comment";
6560  }
6561 */
6562  if(Entry[1] != '*') //changed from 'else' at v2.9.1
6563  {
6564  int SCPos = Entry.Pos(';'); // semicolon
6565  int CPos = Entry.Pos(','); // comma
6566  // 5 possibilities: no comma & no semicolon - just text - enter 1st 12 characters
6567  // both, comma before semicolon, i.e text on its own line, semicolon on next line - e.g. service headcode but no
6568  // description - enter the text up to the comma
6569  // both, semicolon before comma, normal - enter text up to the semicolon
6570  // comma & no semicolon, one or more lines of text without any semicolons - enter the text up to the comma
6571  // semicolon & no comma - enter text up to the semicolon
6572  if((CPos == 0) && (SCPos == 0))
6573  {
6574  Entry = Entry.SubString(1, 12);
6575  }
6576  else if((CPos > 0) && (SCPos > 0) && (CPos < SCPos))
6577  {
6578  Entry = Entry.SubString(1, CPos - 1);
6579  }
6580  else if((CPos > 0) && (SCPos > 0) && (CPos > SCPos))
6581  {
6582  Entry = Entry.SubString(1, SCPos - 1);
6583  }
6584  else if((CPos > 0) && (SCPos == 0))
6585  {
6586  Entry = Entry.SubString(1, CPos - 1);
6587  }
6588  else
6589  {
6590  Entry = Entry.SubString(1, SCPos - 1);
6591  }
6592  }
6593  AllEntriesTTListBox->Items->Add(Entry);
6594  continue;
6595  }
6596  else
6597  {
6598  Segment = PostEnd;
6599  AllEntriesTTListBox->Items->Add("END (Blank)");
6600  continue;
6601  }
6602  }
6603  if(Segment == PostEnd)
6604  {
6605  if(*TEVIterator == "")
6606  {
6607  AllEntriesTTListBox->Items->Add("+ Blank");
6608  }
6609  else
6610  {
6611  AnsiString CurrentStr = *TEVIterator;
6612  if(CurrentStr != "") // strip any non alphanumeric characters (specifically \r or \n)
6613  {
6614  CurrentStr = CurrentStr.SubString(1, 10);
6615  for(int x = 1; x < CurrentStr.Length(); x++)
6616  {
6617  if((CurrentStr[x] < 32) || (CurrentStr[1] > 126))
6618  {
6619  CurrentStr = CurrentStr.SubString(1, (x - 1));
6620  }
6621  }
6622  }
6623  AllEntriesTTListBox->Items->Add("+ " + CurrentStr);
6624  }
6625  continue;
6626  }
6627  }
6629  {
6630  TTStartTimeBox->Text = "";
6631  }
6632  TTCurrentEntryIterator = TTLastServiceIterator; // may well be reset outside this function but need to ensure that it has a valid value on exit, even if it's null
6633  Utilities->CallLogPop(1680);
6634 }
6635 // ---------------------------------------------------------------------------
6636 
6637 void __fastcall TInterface::AZOrderButtonClick(TObject *Sender)
6638 {
6639  try
6640  {
6641  if(TimetableEditVector.empty())
6642  {
6643  return; // shouldn't be able to access this if it is but keep in for safety
6644  }
6645  TrainController->LogEvent("AZOrderClick");
6646  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",AZOrderClick");
6647  if(AZOrderButton->Caption == AnsiString("A-Z Order"))
6648  {
6649  TTEVIterator SortStart, SortEnd;
6650  UnicodeString MessageStr =
6651  "If you wish to preserve the original order don't save any changes whilst in alphabetical order.\n\n"
6652  "To preserve the original order use alphabetical order to find the service required, click it to display it,"
6653  " then revert to the original order where the same service will be displayed and can be changed.\n\n"
6654  "This warning won't be shown again but a new warning will be given on attempting to save when in alphabetical order.";
6655  if(!AZWarningSent)
6656  {
6657  Application->MessageBox(MessageStr.c_str(), L"IMPORTANT:", MB_OK | MB_ICONWARNING);
6658  AZWarningSent = true;
6659  }
6662  SortStart = TimetableEditVector.begin(); // if no start time set sort from beginning
6664  {
6665  SortStart = TTFirstServiceIterator;
6666  }
6667  SortEnd = TimetableEditVector.end(); // if no last service set sort to end
6669  {
6670  SortEnd = TTLastServiceIterator + 1;
6671  }
6672  std::sort(SortStart, SortEnd);
6674  bool CurrentEntryChanged = false;
6675  for(TTimetableEditVector::iterator x = TimetableEditVector.begin(); x < TimetableEditVector.end(); x++)
6676  {
6677  if(TTSelectedEntry == *x)
6678  {
6680  CurrentEntryChanged = true;
6681  }
6682  }
6683  if(!CurrentEntryChanged)
6684  {
6686  }
6687  AZOrderButton->Caption = AnsiString("Original Order");
6688  AZOrderButton->Hint = AnsiString("Arrange services in original order Toggle with Shift+ Z");
6689  }
6690  else
6691  {
6693  {
6694  UnicodeString MessageStr =
6695  "Reverting to the original order will discard any changes made whilst in alphabetical order.\n\nTo preserve the changes click 'No', then save the timetable or use 'save as' if you wish to keep the original timetable.\n\nDo you wish to proceed?";
6696  int button = Application->MessageBox(MessageStr.c_str(), L"Warning!", MB_YESNO | MB_ICONWARNING);
6697  if(button == IDNO)
6698  {
6699  TimetableChangedFlag = true;
6700  TimetableValidFlag = false;
6702  SetLevel1Mode(135);
6703  Utilities->CallLogPop(2166);
6704  return;
6705  }
6706  }
6710  bool CurrentEntryChanged = false;
6711  for(TTimetableEditVector::iterator x = TimetableEditVector.begin(); x < TimetableEditVector.end(); x++)
6712  {
6713  if(TTSelectedEntry == *x)
6714  {
6716  CurrentEntryChanged = true;
6717  }
6718  }
6719  if(!CurrentEntryChanged)
6720  {
6722  }
6723  AZOrderButton->Caption = AnsiString("A-Z Order");
6724  AZOrderButton->Hint = AnsiString("Arrange services in alphabetical order Toggle with Shift+ Z");
6725  }
6726 // TimetableChangedFlag = true; dropped for v2.11.0
6727  TimetableValidFlag = false;
6730  SetLevel1Mode(136);
6731  Utilities->CallLogPop(2165);
6732  }
6733  catch(const Exception &e) //non-error catch
6734  {
6735  ShowMessage("The following error has occurred: " + e.Message + ".\n\nPlease don't save the timetable as it has probably been corrupted - "
6736  "especially likely if the timetable hadn't been validated.\n\nInstead please exit the timetable editor and reload the original."
6737  "\n\nIf the problem persists with a validated timetable please send the railway and original timetable to railwayfeedback@gmail.com"
6738  " - thanks.");
6739  // ErrorLog(211, e.Message);
6740  }
6741 }
6742 
6743 // ---------------------------------------------------------------------------
6744 
6745 void __fastcall TInterface::TimeOrderButtonClick(TObject *Sender)
6746 {
6747  try
6748  {
6749  if(TimetableEditVector.empty())
6750  {
6751  return; // shouldn't be able to access this if it is but keep in for safety
6752  }
6753  TrainController->LogEvent("TimeOrderClick");
6754  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TimeOrderClick");
6755  if(TimeOrderButton->Caption == AnsiString("Time Order"))
6756  {
6757  TTEVIterator SortStart, SortEnd;
6758  UnicodeString MessageStr =
6759  "If you wish to preserve the original order don't save any changes whilst in time order.\n\n"
6760  "This warning won't be shown again but a new warning will be given on attempting to save when in time order.";
6761  if(!TimeWarningSent)
6762  {
6763  Application->MessageBox(MessageStr.c_str(), L"IMPORTANT:", MB_OK | MB_ICONWARNING);
6764  TimeWarningSent = true;
6765  }
6768 
6769 //addition for time order over AZ order
6770  AnsiString Time = "", StartTime = "";
6771  int StartPos = 0;
6772  bool PreStart = true, MainBody = false, PostEnd = false;
6773  int IteratorCount = 0; //use this instead of an explicit iterator because vector likely to be reallocated when entries increased in size
6774  while(true)
6775  {
6776  if((TimetableEditVector.begin() + IteratorCount) == TimetableEditVector.end())
6777  {
6778  break;
6779  }
6780  TTimetableEditVector::iterator x = TimetableEditVector.begin() + IteratorCount;
6781  if(PreStart)
6782  {
6783  StartTime = (*x).SubString(1,5);
6784  if(((StartTime[1] >= '0') && (StartTime[1] <= '9')) &&
6785  ((StartTime[2] >= '0') && (StartTime[2] <= '9')) &&
6786  ((StartTime[3] == ':')))
6787  {
6788  *x = "//:58" + (*x);
6789  PreStart = false;
6790  MainBody = true;
6791  IteratorCount++;
6792  }
6793  else
6794  {
6795  *x = "//:57" + (*x);
6796  IteratorCount++;
6797  }
6798  }
6799  else if(MainBody)
6800  {
6801  bool AllCommas = true;
6802  for(int a = 1; a < (*x).Length() + 1; a++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
6803  {
6804  if((*x)[a] != ',')
6805  {
6806  AllCommas = false;
6807  }
6808  }
6809  if((*x == "") || AllCommas) //end of MainBody, test for line with one or more commas, added after 2.19.1 when Micke's Uppsala railway had spurious
6810  { //comma at end but it validated ok. Not clear how got there but it did so it could happen again
6811  MainBody = false; //may have all commas if used Excel (this also used on ProcessOneTimetableLine)
6812  PostEnd = true;
6813  *x = "::::;";
6814  IteratorCount++;
6815  continue;
6816  }
6817  if((*x)[1] == '*')
6818  {
6819  *x = "//:59" + (*x); //so all go up front after start time
6820  IteratorCount++;
6821  continue;
6822  }
6823  StartPos = (*x).Pos(";Snt");
6824  if(StartPos == 0)
6825  {
6826  StartPos = (*x).Pos(";Sns");
6827  }
6828  if(StartPos == 0)
6829  {
6830  StartPos = (*x).Pos(";Sfs");
6831  }
6832  if((StartPos == 0) || (StartPos < 11)) //11 allows for min length of headcode (4), comma (1), time (5) and the ';' (1)
6833  {
6834  throw Exception("No start entry or other error in " + (*x));
6835  }
6836  //now work forwards from the last comma to make sure there's room for the time
6837  int y = StartPos;
6838  while((y > 0) && ((*x)[y] != ','))
6839  {
6840  y--;
6841  }
6842  if(y == 0)
6843  {
6844  throw Exception("No comma prior to time entry in " + (*x));
6845  }
6846  else if((StartPos - y) < 6) //StartPos -> ';' and y -> ',' so if less than 6 there's no room for the time
6847  {
6848  throw Exception("No room for the time entry in " + (*x));
6849  }
6850  Time = (*x).SubString(StartPos - 5,5);
6851  *x = Time + (*x);
6852  IteratorCount++;
6853  }
6854  else if(PostEnd)
6855  {
6856  *x = ":::;;" + (*x);
6857  IteratorCount++;
6858  }
6859  else
6860  {
6861  throw Exception("Can't identify entry " + (*x));
6862  }
6863  }
6864 //end of addition
6865 
6866  SortStart = TimetableEditVector.begin(); // if no start time set sort from beginning
6868  {
6869  SortStart = TTFirstServiceIterator;
6870  }
6871  SortEnd = TimetableEditVector.end(); // if no last service set sort to end
6873  {
6874  SortEnd = TTLastServiceIterator + 1;
6875  }
6876  std::sort(SortStart, SortEnd);
6877 //addition for time order over AZ order
6878 //now remove the times at start of each entry
6879  for(TTimetableEditVector::iterator x = TimetableEditVector.begin(); x < TimetableEditVector.end(); x++)
6880  {
6881  *x = (*x).SubString(6, (*x).Length() - 5);
6882  }
6883 //end of addition
6885  bool CurrentEntryChanged = false;
6886  for(TTimetableEditVector::iterator x = TimetableEditVector.begin(); x < TimetableEditVector.end(); x++)
6887  {
6888  if(TTSelectedEntry == *x)
6889  {
6891  CurrentEntryChanged = true;
6892  }
6893  }
6894  if(!CurrentEntryChanged)
6895  {
6897  }
6898  TimeOrderButton->Caption = AnsiString("Original Order");
6899  TimeOrderButton->Hint = AnsiString("Arrange services in original order Toggle with Shift+ Y");
6900  }
6901  else
6902  {
6904  {
6905  UnicodeString MessageStr =
6906  "Reverting to the original order will discard any changes made whilst in time order.\n\nTo preserve the changes click 'No', "
6907  "then save the timetable or use 'save as' if you wish to keep the original timetable.\n\nDo you wish to proceed?";
6908  int button = Application->MessageBox(MessageStr.c_str(), L"Warning!", MB_YESNO | MB_ICONWARNING);
6909  if(button == IDNO)
6910  {
6911  TimetableChangedFlag = true;
6912  TimetableValidFlag = false;
6914  SetLevel1Mode(141);
6915  Utilities->CallLogPop(2691);
6916  return;
6917  }
6918  }
6922  bool CurrentEntryChanged = false;
6923  for(TTimetableEditVector::iterator x = TimetableEditVector.begin(); x < TimetableEditVector.end(); x++)
6924  {
6925  if(TTSelectedEntry == *x)
6926  {
6928  CurrentEntryChanged = true;
6929  }
6930  }
6931  if(!CurrentEntryChanged)
6932  {
6934  }
6935  TimeOrderButton->Caption = AnsiString("Time Order");
6936  TimeOrderButton->Hint = AnsiString("Arrange services in chronological (time) order Toggle with Shift+ Y");
6937  }
6938 // TimetableChangedFlag = true; dropped for v2.11.0
6939  TimetableValidFlag = false;
6942  SetLevel1Mode(142);
6943  Utilities->CallLogPop(2692);
6944  }
6945  catch(const Exception &e) //non-error catch
6946  {
6947  ShowMessage("The following error has occurred: " + e.Message + ".\n\nPlease don't save the timetable as it has probably been corrupted - "
6948  "especially likely if the timetable hadn't been validated.\n\nInstead please exit the timetable editor and reload the original."
6949  "\n\nIf the problem persists with a validated timetable please send the railway and original timetable to railwayfeedback@gmail.com"
6950  " - thanks.");
6951 // ErrorLog(, e.Message);
6952  }
6953 }
6954 
6955 //---------------------------------------------------------------------------
6956 
6957 void TInterface::ConvertCRLFsToCommas(int Caller, AnsiString &ConvStr)
6958 {
6959  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConvertCRLFsToCommas," + ConvStr);
6960  AnsiString OutStr = "";
6961  int x = 1; // AnsiString arrays start at 1
6962 
6963  while(x < ConvStr.Length()) // skip the last character as looking for CRLF pairs, i.e. '\r' followed by '\n'
6964  {
6965  if((ConvStr[x] == '\r') && (ConvStr[x + 1] == '\n'))
6966  {
6967  OutStr += ',';
6968  x++;
6969  x++;
6970  }
6971  else
6972  {
6973  OutStr += ConvStr[x];
6974  x++;
6975  }
6976  }
6977  if(x == ConvStr.Length())
6978  {
6979  OutStr += ConvStr[x]; // add the last character
6980 
6981  }
6982 // strip any excess commas from the end
6983  if(OutStr != "")
6984  {
6985  while(OutStr[OutStr.Length()] == ',')
6986  {
6987  OutStr = OutStr.SubString(1, OutStr.Length() - 1);
6988  if(OutStr == "")
6989  {
6990  break; // if consisted of just commas then without this would fail on range error when becomes a null string
6991  }
6992  }
6993  }
6994  ConvStr = OutStr;
6995  if(ConvStr == "")
6996  {
6997  ConvStr = ','; // don't return a null or will fail, OK to return a comma on its own as will be ignored during ProcessOneTimetableLine when AllCommas will be true
6998  }
6999  Utilities->CallLogPop(1846);
7000 }
7001 
7002 // ---------------------------------------------------------------------------
7003 
7005 {
7006 /* CreateEditTTFileName set if a TT file loaded (even if empty), the iterators TTStartTimeIterator, TTFirstServiceIterator, TTLastServiceIterator provide
7007  relevant information - if set to ...end() then not set. TTCurrentEntryIterator is set to the Entry to be displayed or null if there's no start time or no
7008  entries
7009 */
7010  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TimetableHandler");
7011  PreviousTTEntryButton->Enabled = false;
7012  NextTTEntryButton->Enabled = false;
7013  AddMinsButton->Enabled = false;
7014  SubMinsButton->Enabled = false;
7015  CopyTTEntryButton->Enabled = false;
7016  CutTTEntryButton->Enabled = false;
7017  PasteTTEntryButton->Enabled = false;
7018  DeleteTTEntryButton->Enabled = false;
7019  SaveTTEntryButton->Enabled = false;
7020  SaveTTButton->Enabled = false;
7021  SaveTTAsButton->Enabled = false;
7022  ValidateTimetableButton->Enabled = false;
7023  AZOrderButton->Enabled = false;
7024  TimeOrderButton->Enabled = false;
7025  TTServiceSyntaxCheckButton->Enabled = false;
7026  InvertTTEntryButton->Enabled = false;
7027  NewTTEntryButton->Enabled = false;
7028  MoveTTEntryUpButton->Enabled = false;
7029  MoveTTEntryDownButton->Enabled = false;
7030  CancelTTEntryButton->Enabled = false;
7031  RestoreTTButton->Enabled = false;
7032  ExportTTButton->Enabled = false;
7033  ExpandRepeatsButton->Enabled = false;
7034  ConflictAnalysisButton->Enabled = false;
7035  ExitTTModeButton->Enabled = true;
7036 
7038  {
7039  AZOrderButton->Enabled = true;
7040  TimeOrderButton->Enabled = true;
7041  }
7042  if(AZOrderButton->Caption == "Original Order")
7043  {
7044  TimeOrderButton->Enabled = false;
7045  }
7046  if(TimeOrderButton->Caption == "Original Order")
7047  {
7048  AZOrderButton->Enabled = false;
7049  }
7051  {
7052  TimetableValidFlag = false; // should always be the case anyway but include here to be sure
7053  }
7054  if(CreateEditTTFileName == "")
7055  {
7056  TimetableNameLabel->Caption = "Creating new timetable: not yet saved";
7057  }
7058  else
7059  {
7060  TimetableNameLabel->Caption = "Editing timetable: " + CreateEditTTTitle;
7061  }
7062  if(TTStartTimeIterator != TimetableEditVector.end()) // Null means start time not yet set
7063  {
7064  TTStartTimeBox->Text = (*TTStartTimeIterator).SubString(1, 5); // 1st 5 chars = time if validity check OK
7065  }
7066 // start time now set & displayed
7067 
7068  AnsiString AnsiAddSubText(AddSubMinsBox->Text); //this section moved here from below at v2.20.1 so these buttons available most of the time
7069  if((AnsiAddSubText != "") && AreAnyTimesInCurrentEntry())
7070  {
7071  bool ValidFlag = true;
7072  for(int x = 1; x <= AnsiAddSubText.Length(); x++)
7073  {
7074  if((AnsiAddSubText[x] > '9') || (AnsiAddSubText[x] < '0'))
7075  {
7076  ValidFlag = false;
7077  break;
7078  }
7079  }
7080  if(ValidFlag)
7081  {
7082  if(AnsiAddSubText.ToInt() != 0)
7083  {
7084  AddMinsButton->Enabled = true;
7085  SubMinsButton->Enabled = true;
7086  }
7087  }
7088  } //end of section moved
7090  {
7091  InfoPanel->Visible = true;
7092  InfoPanel->Caption = "Select option or change entry";
7093  if(RailwayTitle != "")
7094  {
7095  ShowHideTTButton->Enabled = true;
7096  }
7097  else
7098  {
7099  ShowHideTTButton->Enabled = false;
7100  }
7101  ExitTTModeButton->Enabled = true;
7102  AllEntriesTTListBox->Enabled = true;
7103 // AnsiString AnsiAddSubText(AddSubMinsBox->Text); //moved to earlier at v2.20.1
7104 // if((AnsiAddSubText != "") && AreAnyTimesInCurrentEntry())
7105 // {
7106 // bool ValidFlag = true;
7107 // for(int x = 1; x <= AnsiAddSubText.Length(); x++)
7108 // {
7109 // if((AnsiAddSubText[x] > '9') || (AnsiAddSubText[x] < '0'))
7110 // {
7111 // ValidFlag = false;
7112 // break;
7113 // }
7114 // }
7115 // if(ValidFlag)
7116 // {
7117 // if(AnsiAddSubText.ToInt() != 0)
7118 // {
7119 // AddMinsButton->Enabled = true;
7120 // SubMinsButton->Enabled = true;
7121 // }
7122 // }
7123 // }
7125  {
7126  RestoreTTButton->Enabled = true;
7127  }
7128  if(!TimetableValidFlag && RlyFile && !TimetableChangedFlag && (CreateEditTTFileName != "") && (AZOrderButton->Caption != "Original Order")
7129  && (TimeOrderButton->Caption != "Original Order"))//added AZOrder & TimeOrder conditions at v2.20.0 to prevent Validate being offered when order changed
7130  { //because it validates the timetable text file which hasn't changed until saved so the message given is misleading
7131  // Need !TimetableChangedFlag because the changed TT must be saved before validation - it's the TT file that is checked
7132  // so if it is changed but not saved, the 'correct' file will check OK but the changed TT may well not be valid
7133  ValidateTimetableButton->Enabled = true;
7134  }
7136  {
7137  ExportTTButton->Enabled = true;
7138  ConflictAnalysisButton->Enabled = true;
7139  if((*TTCurrentEntryIterator).Pos(",R;") > 0)
7140  {
7141  ExpandRepeatsButton->Enabled = true;
7142  }
7143  }
7145  {
7146  CopyTTEntryButton->Enabled = true;
7147  CutTTEntryButton->Enabled = true;
7148  DeleteTTEntryButton->Enabled = true;
7149  }
7150  if(!TimetableEditVector.empty() && (TimetableChangedFlag || (AZOrderButton->Caption == "Original Order")
7151  || (TimeOrderButton->Caption == "Original Order"))) //added AZOrder & TimeOrder conditions at v2.20.0 to allow saving in new order - warning given if click
7152  {
7153  SaveTTButton->Enabled = true;
7154  }
7155  if(!TimetableEditVector.empty())
7156  {
7157  SaveTTAsButton->Enabled = true;
7158  }
7160  {
7161  NewTTEntryButton->Enabled = true;
7162  }
7164  {
7166  {
7167  NextTTEntryButton->Enabled = true;
7168  MoveTTEntryDownButton->Enabled = true;
7169  }
7171  {
7172  PreviousTTEntryButton->Enabled = true;
7173  MoveTTEntryUpButton->Enabled = true;
7174  }
7175  }
7177  {
7178  if(*TTCurrentEntryIterator != "")
7179  {
7181  {
7182  TTServiceSyntaxCheckButton->Enabled = true;
7183  InvertTTEntryButton->Enabled = true;
7184  }
7185  }
7186  }
7187  if(CopiedEntryFlag)
7188  {
7189  PasteTTEntryButton->Enabled = true;
7190  }
7191  OneEntryTimetableMemo->Clear(); // don't clear if Entry changed
7193  {
7194 // if(*TTCurrentEntryIterator != "") don't use this here as fails to highlight blank line entries, but need to add it in next condition or can have error (Cameron020723errorlog.err)
7196  { //added (*TTCurrentEntryIterator != "") at v2.16.0 due to Cameron's error noted above
7197  /* Details of the error: Fault arose because a timetable had a start time without any entries other than comments,
7198  and a blank line was saved at the end. The program has a number of internal timetable service iterators (an iterator is a type
7199  of memory address pointer) which point to the start time entry, the first service entry, the last service entry, the current
7200  (i.e. user-selected entry) and so on. In earlier versions these were initially set to null (i.e zero - didn't point to anything), but in
7201  v2.15.0 I changed them to point to the next (unused) service location because the 64 bit compiler won't accept
7202  null as an iterator value. Interestingly I set them to an unused location deliberately to force an error if the program
7203  tried to use it as a real location, and in Cameron's timetable it did.
7204  */
7205  bool ServiceEntry = true;
7207  }
7208  else
7209  {
7210  bool ServiceEntry = false;
7212  }
7213  }
7214  }
7215  else
7216  {
7217  CancelTTEntryButton->Enabled = true;
7218  SaveTTEntryButton->Enabled = true;
7219  ShowHideTTButton->Enabled = false;
7220  ExitTTModeButton->Enabled = false;
7221  AllEntriesTTListBox->Enabled = false; // to stop entries being selected
7222  InfoPanel->Caption = "Add or change entry then save it, or cancel";
7223  InfoPanel->Visible = true;
7224  }
7225  Utilities->CallLogPop(1600);
7226 }
7227 
7228 // ---------------------------------------------------------------------------
7229 void TInterface::DisplayOneTTLineInPanel(int Caller, AnsiString Data, bool ServiceEntry)
7230 {
7231  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DisplayOneTTLineInPanel," + Data + ", " +
7232  AnsiString((short)ServiceEntry));
7233  OneEntryTimetableMemo->Clear();
7234  if(ServiceEntry)
7235  {
7236  TrainController->StripSpaces(1, Data);
7237  while(true)
7238  {
7239  int CommaPos = Data.Pos(',');
7240  if((CommaPos == 0) && (Data != ""))
7241  {
7242  CommaPos = Data.Length() + 1;
7243  }
7244  OneEntryTimetableMemo->Lines->Add(Data.SubString(1, CommaPos - 1));
7245  if(Data.Length() <= CommaPos)
7246  {
7247  break;
7248  }
7249  Data = Data.SubString(CommaPos + 1, Data.Length() - CommaPos);
7250  }
7251  }
7252  else
7253  {
7254  OneEntryTimetableMemo->Text = Data;
7255  }
7256  int TotalLines = OneEntryTimetableMemo->Lines->Count; // remove excess lines at bottom
7257 
7258  while((OneEntryTimetableMemo->Lines->Strings[TotalLines - 1] == "") || (OneEntryTimetableMemo->Lines->Strings[TotalLines - 1] == "\r\n"))
7259  {
7260  OneEntryTimetableMemo->Lines->Delete(TotalLines - 1);
7261  TotalLines--;
7262  if(TotalLines < 1)
7263  {
7264  break;
7265  }
7266  }
7267  OneEntryTimetableMemo->HideSelection = true;
7268  OneEntryTimetableMemo->SelStart = 0; // need this & next command to set cursor to the top
7269  OneEntryTimetableMemo->SelLength = 0;
7271  Utilities->CallLogPop(1602);
7272 }
7273 // ---------------------------------------------------------------------------
7274 
7276 { //not used from v2.13.0 as clicking an entry does all that is required
7277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",HighlightOneEntryInAllEntriesTTListBox," + AnsiString(Position));
7278  if(TimetableEditVector.empty() || (AllEntriesTTListBox->Items->Count == 0))
7279  {
7280  HighlightPanel->Top = 32;
7281  HighlightPanel->Caption = "";
7282  HighlightPanel->Width = 100;
7283  HighlightPanel->Visible = false;
7284  }
7285  else
7286  {
7287  AnsiString CurrentStr = AllEntriesTTListBox->Items->Strings[Position];
7288  if(CurrentStr != "") // strip any non alphanumeric characters (specifically \r or \n)
7289  {
7290  for(int x = 1; x < CurrentStr.Length(); x++)
7291  {
7292  if((CurrentStr[x] < 32) || (CurrentStr[1] > 126))
7293  {
7294  CurrentStr = CurrentStr.SubString(1, (x - 1));
7295  }
7296  }
7297  }
7298  HighlightPanel->Top = 32 + (Position * 13) - (AllEntriesTTListBox->TopIndex * 13);
7299  if(HighlightPanel->Top < 32)
7300  {
7301  HighlightPanel->Visible = false;
7302  }
7303  else
7304  {
7305  HighlightPanel->Visible = true; // doesn't matter if goes off the bottom as it becomes invisible as then it's off its parent panel
7306  }
7307  HighlightPanel->Caption = CurrentStr;
7308  if(AllEntriesTTListBox->Items->Count > 47) // because the scrollbar will be present
7309  {
7310  HighlightPanel->Width = 82;
7311  }
7312  else
7313  {
7314  HighlightPanel->Width = 100;
7315  }
7316  }
7317  Utilities->CallLogPop(1709);
7318 }
7319 
7320 // ---------------------------------------------------------------------------
7322 {
7324  {
7325  return(false);
7326  }
7327  TDateTime DummyTime;
7328  bool TimesPresent = false;
7329 
7330  for(int x = 0; x < OneEntryTimetableMemo->Lines->Count; x++)
7331  {
7332  for(int y = 1; y < (OneEntryTimetableMemo->Lines->Strings[x].Length() - 3); y++)
7333  {
7334  if(TrainController->CheckTimeValidity(20, OneEntryTimetableMemo->Lines->Strings[x].SubString(y, 5), DummyTime))
7335  {
7336  TimesPresent = true;
7337  break;
7338  }
7339  }
7340  if(TimesPresent)
7341  {
7342  break;
7343  }
7344  }
7345  return(TimesPresent);
7346 }
7347 
7348 // ---------------------------------------------------------------------------
7349 // end of Timetable editing functions
7350 // ---------------------------------------------------------------------------
7351 void __fastcall TInterface::ExitMenuItemClick(TObject *Sender)
7352 {
7353  try
7354  {
7355  TrainController->LogEvent("ExitMenuItemClick");
7356  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ExitMenuItemClick");
7357 /* Dropped at v2.9.1 as serves no apparent purpose
7358  if(!FileChangedFlag && !(Track->IsTrackFinished()) && (EveryPrefDir->PrefDirSize() > 0))
7359  {
7360  UnicodeString MessageStr =
7361  "Note that leaving the track unlinked will cause preferred directions to be lost on reloading. Prevent by linking the track then resaving. Do you still wish to exit?";
7362  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
7363  if(button == IDNO)
7364  {
7365  Utilities->CallLogPop(1711);
7366  return;
7367  }
7368  }
7369 */
7370  if(FileChangedFlag)
7371  {
7372  UnicodeString MessageStr = "The railway has changed, exit without saving?";
7373  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
7374  if(button == IDNO)
7375  {
7376  Utilities->CallLogPop(1180);
7377  return;
7378  }
7379  }
7380  if((TempTTFileName != "") && FileExists(TempTTFileName))
7381  {
7382  DeleteFile(TempTTFileName);
7383  }
7384  Utilities->CallLogPop(1181);
7385  Application->Terminate();
7386  }
7387  catch(const Exception &e)
7388  {
7389  ErrorLog(140, e.Message);
7390  }
7391 }
7392 // ---------------------------------------------------------------------------
7393 
7394 void __fastcall TInterface::TrackInfoOnOffMenuItemClick(TObject *Sender)
7395 {
7396  try
7397  {
7398  TrainController->LogEvent("TrackInfoOnOffMenuItemClick");
7399  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TrackInfoOnOffMenuItemClick");
7400  if(!TrackInfoShowing)
7401  {
7402  TrackInfoOnOffMenuItem->Caption = "Hide Track &Information";
7403  TrackInfoShowing = true;
7404  }
7405  else
7406  {
7407  TrackInfoOnOffMenuItem->Caption = "Show Track &Information";
7408  TrackInfoShowing = false;
7409  }
7410  Utilities->CallLogPop(1183);
7411  }
7412  catch(const Exception &e)
7413  {
7414  ErrorLog(173, e.Message);
7415  }
7416 }
7417 // ---------------------------------------------------------------------------
7418 
7419 void __fastcall TInterface::TrainStatusInfoOnOffMenuItemClick(TObject *Sender)
7420 {
7421  try
7422  {
7423  TrainController->LogEvent("TrainStatusInfoOnOffMenuItemClick");
7424  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TrainStatusInfoOnOffMenuItemClick");
7425  if(!TrainStatusShowing)
7426  {
7427  TrainStatusInfoOnOffMenuItem->Caption = "Hide Train &Status Information";
7428  TrainStatusShowing = true;
7429  }
7430  else
7431  {
7432  TrainStatusInfoOnOffMenuItem->Caption = "Show Train &Status Information";
7433  TrainStatusShowing = false;
7434  }
7435  Utilities->CallLogPop(1184);
7436  }
7437  catch(const Exception &e)
7438  {
7439  ErrorLog(141, e.Message);
7440  }
7441 }
7442 
7443 // ---------------------------------------------------------------------------
7444 void __fastcall TInterface::TrainTTInfoOnOffMenuItemClick(TObject *Sender)
7445 {
7446  try
7447  {
7448  TrainController->LogEvent("TrainTTInfoOnOffMenuItemClick");
7449  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TrainTTInfoOnOffMenuItemClick");
7450  if(!TrainTTShowing)
7451  {
7452  TrainTTInfoOnOffMenuItem->Caption = "Hide Train &Timetable Information";
7453  TrainTTShowing = true;
7454  }
7455  else
7456  {
7457  TrainTTInfoOnOffMenuItem->Caption = "Show Train &Timetable Information";
7458  TrainTTShowing = false;
7459  }
7460  Utilities->CallLogPop(1185);
7461  }
7462  catch(const Exception &e)
7463  {
7464  ErrorLog(142, e.Message);
7465  }
7466 }
7467 
7468 // ---------------------------------------------------------------------------
7469 // Mouse Functions
7470 // ---------------------------------------------------------------------------
7471 void __fastcall TInterface::MainScreenMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
7472 // caller function - stops master clock
7473 {
7474 // have to allow in zoom out mode
7475  try
7476  {
7477  AnsiString AnsiButton = "mbLeft";
7478  if(Button == mbRight)
7479  {
7480  AnsiButton = "mbRight";
7481  }
7482 
7483  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MainScreenMouseDown," + AnsiButton + "," + AnsiString(X) + "," + AnsiString(Y));
7484  bool ClockState = Utilities->Clock2Stopped;
7485  Utilities->Clock2Stopped = true;
7486 
7487  RestoreFocusPanel->Enabled = true; // these added at v2.0.0 to restore navigation keys to move screen when a panel had focus
7488  RestoreFocusPanel->Visible = true; // because then these buttons just cycled through the panel buttons. Added in place of the
7489  RestoreFocusPanel->SetFocus(); // section in ClockTimer2 where focus restored every clock cycle, because then the help screen
7490  RestoreFocusPanel->Visible = false; // was hidden. At least now help is only hidden when the screen clicked, which is normal
7491  RestoreFocusPanel->Enabled = false; // behaviour, and can tell user that can restore navigation keys just by clicking the screen
7492  MMoveTrackSelFlag = false;
7493  MMovePrefDirSelFlag = false;
7497  HideTTActionsListBox(5); //added at v2.19.0 to get rid of these if they were visible
7499 
7501  {
7502  if(!Display->ZoomOutFlag)
7503  {
7504  MainScreenMouseDown2(0, Button, Shift, X, Y);
7505  }
7506  else
7507  {
7508  MainScreenMouseDown3(0, Button, Shift, X, Y);
7509  }
7510  }
7511  Utilities->Clock2Stopped = ClockState;
7512  Utilities->CallLogPop(33);
7513  }
7514  catch(const Exception &e)
7515  {
7516  ErrorLog(19, e.Message);
7517  }
7518 }
7519 
7520 // ---------------------------------------------------------------------------
7521 void TInterface::MainScreenMouseDown2(int Caller, TMouseButton Button, TShiftState Shift, int X, int Y)
7522 {
7523  try
7524  {
7525  AnsiString AnsiButton = "mbLeft";
7526  if(Button == mbRight)
7527  {
7528  AnsiButton = "mbRight";
7529  }
7530  TrainController->LogEvent("MainScreenMouseDown2," + AnsiButton + "," + AnsiString(X) + "," + AnsiString(Y));
7531  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",MainScreenMouseDown2," + AnsiButton + "," + AnsiString(X) +
7532  "," + AnsiString(Y));
7533  // unplot GapFlash graphics if plotted & cancel gap flashing if left mouse button pressed (so can move display with right mouse button)
7534  // but not in ZoomOut mode - so can switch between modes & keep gaps flashing
7535  HideTTActionsListBox(0); //get rid of this for any click on screen
7536  if(Track->GapFlashFlag && !Display->ZoomOutFlag && (Button == mbLeft))
7537  {
7540  Track->GapFlashFlag = false;
7541  }
7542  int HLoc, VLoc;
7543  Track->GetTrackLocsFromScreenPos(1, HLoc, VLoc, X, Y);
7544  int NoOffsetX, NoOffsetY;
7545  Track->GetTruePositionsFromScreenPos(0, NoOffsetX, NoOffsetY, X, Y);
7546  if(Button == mbRight) // track, PrefDir or text erase, PrefDir/route truncate, or take signaller control of train
7547  {
7548  // this routine new at v2.1.0. Allows railway moving for zoom-in mode when no element at HLoc & VLoc
7549  int Dummy; // unused in next function
7550  AnsiString Text = ""; // needed for TextFound but not used
7553  if(!Track->TrackElementPresentAtHV(0, HLoc, VLoc) && !Track->InactiveTrackElementPresentAtHV(0, HLoc, VLoc) && !Track->UserGraphicPresentAtHV(0, X,
7554  Y, Dummy) && !TextHandler->TextFound(0, X + (Display->DisplayOffsetH * 16), Y + (Display->DisplayOffsetV * 16), Text))
7555  {
7558  WholeRailwayMoving = true;
7559  Screen->Cursor = TCursor(-22); // Four arrows;
7560  AnsiString OldInfo = InfoPanel->Caption; //added at v2.22.0
7561  InfoPanel->Caption = "Railway moving, please don't click the mouse"; //added at v2.22.0
7562  InfoPanel->Visible = true;
7563  Display->Update(); //to make the InfoPanel visible
7564  }
7565 
7566  else if(Level2TrackMode == AddText)
7567  {
7568  TrainController->LogEvent("mbRight + AddText");
7569 // ResetChangedFileDataAndCaption(, true); moved from here after 2.7.0 in case no changes made
7570  if(TextHandler->TextFound(1, NoOffsetX, NoOffsetY, Text))
7571  {
7572  if(TextHandler->TextErase(0, NoOffsetX, NoOffsetY, Text)) // erase text in vector
7573  {
7574  ResetChangedFileDataAndCaption(2, true); // moved to here after 2.7.0
7576  if(NoRailway())
7577  {
7578  EditMenu->Enabled = false;
7579  }
7580  else
7581  {
7582  EditMenu->Enabled = true;
7583  }
7584  }
7585  }
7586  SetLevel2TrackMode(57); // to remove 'move text' if last text item removed
7587  Utilities->CallLogPop(34);
7588  return;
7589  }
7590 
7591  else if(Level2TrackMode == AddGraphic)
7592  {
7593  TrainController->LogEvent("mbRight + AddGraphic");
7594  if(Track->UserGraphicVector.empty()) // no user graphics
7595  {
7596  Utilities->CallLogPop(2180);
7597  return;
7598  }
7599  int UGIVecPos;
7600  if(Track->UserGraphicPresentAtHV(1, X, Y, UGIVecPos))
7601  {
7602  Track->UserGraphicVector.erase(Track->UserGraphicVector.begin() + UGIVecPos);
7604  if(NoRailway())
7605  {
7606  EditMenu->Enabled = false;
7607  }
7608  else
7609  {
7610  EditMenu->Enabled = true;
7611  }
7612  }
7613  Utilities->CallLogPop(2181);
7614  return;
7615  }
7616 
7617  else if(Level2TrackMode == AddTrack)
7618  {
7619  TrainController->LogEvent("mbRight + AddTrack");
7620  bool TrackEraseSuccessfulFlag;
7621  int ErasedTrackVectorPosition;
7622  Screen->Cursor = TCursor(-11); // Hourglass;
7623  Track->EraseTrackElement(1, HLoc, VLoc, ErasedTrackVectorPosition, TrackEraseSuccessfulFlag, true);
7624  if(TrackEraseSuccessfulFlag)
7625  {
7626  if(ErasedTrackVectorPosition > -1) //may have been an inactive element
7627  {
7628  EveryPrefDir->RealignAfterTrackErase(0, ErasedTrackVectorPosition);
7629  }
7632  ClearandRebuildRailway(5); // to ensure location named elements plotted correctly & replot the grid if required
7633  SetGapsButton->Enabled = false; // if conditions have changed need to reset buttons, best not calling SetLevel2TrackMode as that
7634  TrackOKButton->Enabled = false; // calls Clearand.. if gridflag set & takes a long time
7635  if(Track->GapsUnset(1))
7636  {
7637  SetGapsButton->Enabled = true;
7638  }
7639  // only enable if there are gaps still to be set (returns false for no track)
7640  else
7641  {
7642  if(!(Track->NoActiveTrack(0)) && !(Track->IsTrackFinished()))
7643  {
7644  TrackOKButton->Enabled = true;
7645  }
7646  // TrackOK only enabled if track exists, there are no unset gaps, and track not finished
7647  }
7648  if(!(Track->IsTrackFinished())) // can only set lengths for several elements together if TrackFinished
7649  {
7650  SetLengthsButton->Enabled = false;
7651  }
7652 // if(NoRailway()) dropped at v2.6.0 to allow edits during AddTrack
7653 // {
7654 // EditMenu->Enabled = false;
7655 // }
7656 // else
7657  EditMenu->Enabled = true;
7658  }
7659  Screen->Cursor = TCursor(-2); // Arrow
7660  Utilities->CallLogPop(35);
7661  return;
7662  }
7663 
7664  else if(Level2TrackMode == DistanceContinuing) // new for extended distances (similar to PrefDirContinuing)
7665  {
7666  TrainController->LogEvent("mbRight + DistanceContinuing");
7667 // ResetChangedFileDataAndCaption(, true); dropped after 2.7.0 as may only be checking existing distances/speeds. Moved to button clicks in TrackLengthPanel
7668  bool LeadingPointsAtLastElement = false;
7669  if(ConstructPrefDir->GetPrefDirTruncateElement(0, HLoc, VLoc))
7670  {
7671  if(ConstructPrefDir->PrefDirSize() == 0)
7672  {
7674  SetLevel1Mode(64);
7676  SetLevel2TrackMode(51); // calls ClearandRebuildRailway to show length erased & sets back to start
7677  Utilities->CallLogPop(1526);
7678  return;
7679  }
7682  ConstructPrefDir->CalcDistanceAndSpeed(0, OverallDistance, OverallSpeedLimit, LeadingPointsAtLastElement);
7683  if(!LeadingPointsAtLastElement)
7684  {
7685  TrackLengthPanel->Visible = true;
7686  TrackLengthPanel->SetFocus();
7687  InfoPanel->Visible = true;
7688  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Continue or set values (overall length), or right click to cancel/truncate";
7689  RestoreAllDefaultLengthsButton->Enabled = true;
7690  ResetDefaultLengthButton->Enabled = true;
7691  LengthOKButton->Enabled = true;
7692  DistanceBox->Text = AnsiString(OverallDistance);
7693  if(OverallSpeedLimit > -1)
7694  {
7695  SpeedLimitBox->Text = AnsiString(OverallSpeedLimit);
7696  }
7697  else
7698  {
7699  SpeedLimitBox->Text = "Mixed";
7700  }
7701  }
7702  else
7703  {
7704  TrackLengthPanel->Visible = true;
7705  TrackLengthPanel->SetFocus();
7706  InfoPanel->Visible = true;
7707  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Can't end on leading points, continue or truncate";
7708  RestoreAllDefaultLengthsButton->Enabled = false;
7709  ResetDefaultLengthButton->Enabled = false;
7710  LengthOKButton->Enabled = false;
7711  }
7713  }
7714  Utilities->CallLogPop(36);
7715  return;
7716  }
7717 
7718  else if(Level2PrefDirMode == PrefDirContinuing) // truncate
7719  {
7720  TrainController->LogEvent("mbRight + PrefDirContinuing");
7721 // ResetChangedFileDataAndCaption(, false); moved to later after 2.7.0 as may not change anything
7722 // RlyFile = false; - don't alter this just for PrefDir changes
7723  if(ConstructPrefDir->GetPrefDirTruncateElement(1, HLoc, VLoc))
7724  {
7725  if(ConstructPrefDir->PrefDirSize() == 0)
7726  {
7728  SetLevel1Mode(14); // all PrefDir truncated
7729  Utilities->CallLogPop(37);
7730  return;
7731  }
7733  ResetChangedFileDataAndCaption(5, false); // moved to here after 2.7.0
7734  }
7736  SetLevel2PrefDirMode(0); // calls ClearandRebuildRailway to show length erased & sets back to start
7737  Utilities->CallLogPop(38);
7738  return;
7739  }
7740 
7741  else if((Level1Mode == PrefDirMode) && (Level2PrefDirMode != PrefDirContinuing) && (Level2PrefDirMode != PrefDirSelecting)) // delete element
7742  {
7743  TrainController->LogEvent("mbRight + != PrefDirContinuing");
7745 // RlyFile = false; - don't alter this just for PrefDir changes
7748  SetLevel1Mode(15); // calls ClearandRebuildRailway to show length erased & sets back to start
7749  Utilities->CallLogPop(39);
7750  return;
7751  }
7752 
7753  else if((Level2OperMode == Operating) || (Level2OperMode == PreStart)) // disallow when paused, but allow some parts in prestart
7754  {
7755  TrainController->LogEvent("mbRight + OperMode");
7756  bool FoundFlag;
7757  int VecPos = Track->GetVectorPositionFromTrackMap(1, HLoc, VLoc, FoundFlag);
7758  if(FoundFlag && (Level2OperMode != PreStart)) // disallow train popup menu in PreStart
7759  {
7761  // display popup menu for the train
7762  if(SelectedTrainID > -1)
7763  {
7766  if(Train.TrainMode == Signaller)
7767  {
7768  if((Train.LeadElement == -1) || (Track->TrackElementAt(788, Train.LeadElement).Conn[Train.LeadExitPos] == -1)) //train front off continuation or on it
7769  {
7771  }
7772  else if(!Train.Stopped() && (Track->TrackElementAt(1450, (Track->TrackElementAt(1451, Train.LeadElement).Conn[Train.LeadExitPos])).TrackType == Continuation))
7773  { //added at v2.13.0 to prevent popup menu when train moving and front next to continuation
7775  }
7776  }
7777  if((Train.Stopped()) || (Train.TrainFailed && !(Train.TrainMode == Signaller)) ||
7779  !Train.StepForwardFlag))
7780  // don't allow signaller popup menu unless stopped, when failed in timetable mode, or in signaller mode provided that
7781  // train isn't stopping, leaving at a continuation or stepping forward
7782  {
7783  // don't allow selection if another stopped train at a bridge position
7784  if(Track->TrackElementAt(630, VecPos).TrackType == Bridge)
7785  {
7788  if((TrainID01 > -1) && (TrainID23 > -1))
7789  {
7790  TrainController->StopTTClockMessage(0, "Can't select a train at a bridge when another train is at the same bridge");
7791  Utilities->CallLogPop(1103);
7792  return;
7793  }
7794  }
7795  if(Train.TrainMode == Timetable)
7796  {
7797  TakeSignallerControlMenuItem->Enabled = true;
7798  TakeSignallerControlMenuItem->Visible = true;
7799  TimetableControlMenuItem->Enabled = false;
7800  TimetableControlMenuItem->Visible = false;
7801  ChangeDirectionMenuItem->Enabled = false;
7802  ChangeDirectionMenuItem->Visible = false;
7803  SkipTimetabledActionsMenuItem->Enabled = false;
7804  SkipTimetabledActionsMenuItem->Visible = false;
7805  SetReminderMenuItem->Enabled = true;
7806  SetReminderMenuItem->Visible = true;
7807  MoveForwardsMenuItem->Enabled = false;
7808  MoveForwardsMenuItem->Visible = false;
7809  SignallerJoinedByMenuItem->Enabled = false;
7810  SignallerJoinedByMenuItem->Visible = false;
7811  RepairFailedTrainMenuItem->Enabled = false;
7812  RepairFailedTrainMenuItem->Visible = false;
7813  StepForwardMenuItem->Enabled = false;
7814  StepForwardMenuItem->Visible = false;
7815  RemoveTrainMenuItem->Enabled = false;
7816  RemoveTrainMenuItem->Visible = false;
7817  PassRedSignalMenuItem->Enabled = false;
7818  PassRedSignalMenuItem->Visible = false;
7819  SignallerControlStopMenuItem->Enabled = false;
7820  SignallerControlStopMenuItem->Visible = false;
7821  if((Train.StoppedAtSignal || Train.StoppedAtLocation) && !Train.ActionsSkippedFlag && !Train.StoppedWithoutPower)
7822  { //Exclude TreatPassAsTimeLocDeparture, otherwise skipping events causes problems that are best avoided
7823  //'!Train.StoppedWithoutPower' added at v2.19.1
7824  SkipTimetabledActionsMenuItem->Enabled = true;
7825  SkipTimetabledActionsMenuItem->Visible = true;
7826  }
7827  BecomeNewServiceMenuItem->Enabled = false;
7828  BecomeNewServiceMenuItem->Visible = false;
7829  if(IsBecomeNewServiceAvailable(0, SelectedTrainID, Train.FollowOnServiceRef)) //allow this for an unpowered train
7830  {
7831  BecomeNewServiceMenuItem->Enabled = true;
7832  BecomeNewServiceMenuItem->Visible = true;
7833  }
7834  }
7835  else // signaller mode
7836  {
7837  TakeSignallerControlMenuItem->Enabled = false;
7838  TakeSignallerControlMenuItem->Visible = false;
7839  SkipTimetabledActionsMenuItem->Enabled = false;
7840  SkipTimetabledActionsMenuItem->Visible = false;
7841  SetReminderMenuItem->Enabled = false;
7842  SetReminderMenuItem->Visible = false;
7843  BecomeNewServiceMenuItem->Enabled = false;
7844  BecomeNewServiceMenuItem->Visible = false;
7845  if((Train.Crashed) || (Train.Derailed))
7846  {
7847  TimetableControlMenuItem->Enabled = false;
7848  TimetableControlMenuItem->Visible = false;
7849  ChangeDirectionMenuItem->Enabled = false;
7850  ChangeDirectionMenuItem->Visible = false;
7851  MoveForwardsMenuItem->Enabled = false;
7852  MoveForwardsMenuItem->Visible = false;
7853  SignallerJoinedByMenuItem->Enabled = false;
7854  SignallerJoinedByMenuItem->Visible = false;
7855  RepairFailedTrainMenuItem->Enabled = false;
7856  RepairFailedTrainMenuItem->Visible = false;
7857  StepForwardMenuItem->Enabled = false;
7858  StepForwardMenuItem->Visible = false;
7859  PassRedSignalMenuItem->Enabled = false;
7860  PassRedSignalMenuItem->Visible = false;
7861  SignallerControlStopMenuItem->Enabled = false;
7862  SignallerControlStopMenuItem->Visible = false;
7863  RemoveTrainMenuItem->Enabled = true;
7864  RemoveTrainMenuItem->Visible = true;
7865  }
7866  else if(Train.Stopped())
7867  {
7868  if(Train.TimetableFinished)
7869  {
7870  TimetableControlMenuItem->Enabled = false;
7871  TimetableControlMenuItem->Visible = false;
7872  }
7873  else
7874  {
7875  if(Train.RestoreTimetableLocation == "") // en route
7876  {
7877  TimetableControlMenuItem->Enabled = true;
7878  TimetableControlMenuItem->Visible = true;
7879  }
7880  else
7881  {
7882  // obtain train location & check if OK for restoration of tt control
7883  AnsiString LocName = "";
7884  if(Train.LeadElement > -1)
7885  {
7886  LocName = Track->TrackElementAt(802, Train.LeadElement).ActiveTrackElementName;
7887  }
7888  if((LocName == "") && (Train.MidElement > -1))
7889  {
7890  LocName = Track->TrackElementAt(803, Train.MidElement).ActiveTrackElementName;
7891  }
7892  if(Train.RestoreTimetableLocation == LocName)
7893  {
7894  TimetableControlMenuItem->Enabled = true;
7895  TimetableControlMenuItem->Visible = true;
7896  }
7897  else
7898  {
7899  TimetableControlMenuItem->Enabled = false;
7900  TimetableControlMenuItem->Visible = false;
7901  }
7902  }
7903  }
7904 // don't allow ChangeDirection if lead or mid elements (but not lag or next) -1, or lead, mid, lag or next elements continuations
7905  ChangeDirectionMenuItem->Enabled = true;
7906  ChangeDirectionMenuItem->Visible = true;
7907  if(Train.LeadElement > -1)
7908  {
7910  {
7911  ChangeDirectionMenuItem->Enabled = false;
7912  ChangeDirectionMenuItem->Visible = false;
7913  }
7914 /* drop this at v2.12.0, allow cdt for train stopped & leadelement next to exit continuation
7915  if(Track->TrackElementAt(, Train.LeadElement).Conn[Train.LeadExitPos] > -1)
7916  {
7917  if(Track->TrackElementAt(, (Track->TrackElementAt(, Train.LeadElement).Conn[Train.LeadExitPos])).TrackType == Continuation)
7918  {
7919  ChangeDirectionMenuItem->Enabled = false;
7920  ChangeDirectionMenuItem->Visible = false;
7921  }
7922  }
7923 */
7924  }
7925  else
7926  {
7927  ChangeDirectionMenuItem->Enabled = false;
7928  ChangeDirectionMenuItem->Visible = false;
7929  }
7930  if(Train.MidElement > -1) //entering or exiting at a continuation
7931  {
7933  {
7934  ChangeDirectionMenuItem->Enabled = false;
7935  ChangeDirectionMenuItem->Visible = false;
7936  }
7937  }
7938  else
7939  {
7940  ChangeDirectionMenuItem->Enabled = false;
7941  ChangeDirectionMenuItem->Visible = false;
7942  }
7943  if(Train.LagElement > -1)
7944  {
7946  {
7947  ChangeDirectionMenuItem->Enabled = false;
7948  ChangeDirectionMenuItem->Visible = false;
7949  }
7950  }
7951  RemoveTrainMenuItem->Enabled = true;
7952  RemoveTrainMenuItem->Visible = true;
7953  SignallerControlStopMenuItem->Enabled = false;
7954  SignallerControlStopMenuItem->Visible = false;
7955  SignallerJoinedByMenuItem->Enabled = false;
7956  SignallerJoinedByMenuItem->Visible = false;
7957  RepairFailedTrainMenuItem->Enabled = false;
7958  RepairFailedTrainMenuItem->Visible = false;
7959  StepForwardMenuItem->Enabled = false;
7960  StepForwardMenuItem->Visible = false;
7961  MoveForwardsMenuItem->Enabled = false;
7962  MoveForwardsMenuItem->Visible = false;
7963  PassRedSignalMenuItem->Enabled = false;
7964  PassRedSignalMenuItem->Visible = false;
7965  if(Train.AbleToMove(0) && !Train.TreatPassAsTimeLocDeparture) //TreatPassAsTimeLocDeparture added at v2.12.0 to prevent signaller movements
7966  { //for 'pseudo' TimeLoc departures
7967  MoveForwardsMenuItem->Enabled = true;
7968  MoveForwardsMenuItem->Visible = true;
7969  if((!Train.LeavingUnderSigControlAtContinuation) && (Track->TrackElementAt(791, Train.LeadElement).Conn[Train.LeadExitPos] > -1))
7970  { //added check for adjacent element not being a continuation at v2.12.0 as dont allow step forward as wouldn't stop
7972  {
7973  StepForwardMenuItem->Enabled = true; // added 'if' condition for v1.3.2 due to Carwyn Thomas error,
7974  StepForwardMenuItem->Visible = true;
7975  }
7976  }
7977  } // fails on trying to calc AutoSig time delay for resetting signals
7978 
7979  if(Train.AbleToMoveButForSignal(0) && !Train.TreatPassAsTimeLocDeparture) // may not be in AutoSigs route but disallow anyway as not needed at continuation
7980  { //TreatPassAsTimeLocDeparture added at v2.12.0 as above
7981  PassRedSignalMenuItem->Enabled = true;
7982  PassRedSignalMenuItem->Visible = true;
7983  StepForwardMenuItem->Enabled = true;
7984  StepForwardMenuItem->Visible = true;
7985  }
7986  TTrain *AdjacentTrain;
7987  if(Train.IsThereAnAdjacentTrain(0, AdjacentTrain))
7988  {
7989  SignallerJoinedByMenuItem->Enabled = true;
7990  SignallerJoinedByMenuItem->Visible = true;
7991  }
7992  if(Train.TrainFailed)
7993  {
7994  RepairFailedTrainMenuItem->Enabled = true;
7995  RepairFailedTrainMenuItem->Visible = true;
7996  }
7997  }
7998  else // train moving under signaller control - only permit restoration of TT control when stopped as could be in
7999  // mid move, & SetTrainMovementValues only intended to be called when stopped
8000  {
8001  TimetableControlMenuItem->Enabled = false;
8002  TimetableControlMenuItem->Visible = false;
8003  ChangeDirectionMenuItem->Enabled = false;
8004  ChangeDirectionMenuItem->Visible = false;
8005  RemoveTrainMenuItem->Enabled = false;
8006  RemoveTrainMenuItem->Visible = false;
8007  MoveForwardsMenuItem->Enabled = false;
8008  MoveForwardsMenuItem->Visible = false;
8009  SignallerJoinedByMenuItem->Enabled = false;
8010  SignallerJoinedByMenuItem->Visible = false;
8011  RepairFailedTrainMenuItem->Enabled = false;
8012  RepairFailedTrainMenuItem->Visible = false;
8013  PassRedSignalMenuItem->Enabled = false;
8014  PassRedSignalMenuItem->Visible = false;
8015  StepForwardMenuItem->Enabled = false;
8016  StepForwardMenuItem->Visible = false;
8017  SignallerControlStopMenuItem->Enabled = true;
8018  SignallerControlStopMenuItem->Visible = true;
8019  }
8020  }
8021  TrainHeadCodeMenuItem->Caption = Train.HeadCode + ":";
8022  BecomeNewServiceMenuItem->Caption = "Terminate here and become follow-on service " + Train.FollowOnServiceRef; //added at v2.12.0
8023  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
8025  PopupMenu->Popup(MainScreen->Left + X, MainScreen->Top + Y + 43); // menu stops everything so reset timetable time when restarts,
8026  // new at v2.6.1, displays so that can't inadvertently click on a selection if click twice
8027  // 43 is the distance from the top of the screen to the top of TInterface
8028  TrainController->BaseTime = TDateTime::CurrentDateTime();
8030  Utilities->CallLogPop(40);
8031  return;
8032  }
8033  else
8034  {
8035  //allow right click on any train but only to set a reminder - added at v2.19.0
8036  SetReminderMenuItem->Enabled = true;
8037  SetReminderMenuItem->Visible = true;
8038  TimetableControlMenuItem->Enabled = false;
8039  TimetableControlMenuItem->Visible = false;
8040  ChangeDirectionMenuItem->Enabled = false;
8041  ChangeDirectionMenuItem->Visible = false;
8042  MoveForwardsMenuItem->Enabled = false;
8043  MoveForwardsMenuItem->Visible = false;
8044  SignallerJoinedByMenuItem->Enabled = false;
8045  SignallerJoinedByMenuItem->Visible = false;
8046  RepairFailedTrainMenuItem->Enabled = false;
8047  RepairFailedTrainMenuItem->Visible = false;
8048  StepForwardMenuItem->Enabled = false;
8049  StepForwardMenuItem->Visible = false;
8050  PassRedSignalMenuItem->Enabled = false;
8051  PassRedSignalMenuItem->Visible = false;
8052  SignallerControlStopMenuItem->Enabled = false;
8053  SignallerControlStopMenuItem->Visible = false;
8054  RemoveTrainMenuItem->Enabled = false;
8055  RemoveTrainMenuItem->Visible = false;
8056  TakeSignallerControlMenuItem->Enabled = false;
8057  TakeSignallerControlMenuItem->Visible = false;
8058  SkipTimetabledActionsMenuItem->Enabled = false;
8059  SkipTimetabledActionsMenuItem->Visible = false;
8060  BecomeNewServiceMenuItem->Enabled = false;
8061  BecomeNewServiceMenuItem->Visible = false;
8062  TrainHeadCodeMenuItem->Caption = Train.HeadCode + ":";
8063  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
8065  PopupMenu->Popup(MainScreen->Left + X, MainScreen->Top + Y + 43); // menu stops everything so reset timetable time when restarts,
8066  // new at v2.6.1, displays so that can't inadvertently click on a selection if click twice
8067  // 43 is the distance from the top of the screen to the top of TInterface
8068  TrainController->BaseTime = TDateTime::CurrentDateTime();
8070  }
8071  }
8072  }
8073  if(RouteMode == RouteContinuing) // clear a single element (clears whether use left or right mouse button) +allow in PreStart
8074  {
8075  TrainController->LogEvent("mbRight + RouteContinuing");
8077  Utilities->CallLogPop(41);
8078  return;
8079  }
8080 
8081  else if(RouteCancelFlag) // allow in PreStart
8082  {
8083  TrainController->LogEvent("mbRight + RouteCancelFlag");
8084  Screen->Cursor = TCursor(-11); // Hourglass;
8085  // stop clock as sometimes takes several seconds
8086  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
8088  if(AllRoutes->SearchAllRoutesAndTruncate(0, HLoc, VLoc, PreferredRoute)) // updates LockedRouteClass
8089  {
8090  //below addition same as for adding a route and calling on
8091  THVShortPair ExitPair; //added at v2.19.0 to update actions due panel immediately after new route set
8092  if((ActionsDueForm->Visible) && (Level2OperMode == Operating)) // ditto
8093  {
8094  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
8095  {
8096  float LastTimeToExit = TrainController->TrainVectorAt(91, x).TimeToExit;
8097  TrainController->TrainVectorAt(92, x).UpdateTrain(1); //included so 'StoppedAtSignal' flag dropped when route set ahead otherwise
8098  //the 'NOW' entries have to await the next OpTimeToActUpdateCounter update
8099  TrainController->TrainVectorAt(93, x).OpTimeToAct = TrainController->TrainVectorAt(100, x).CalcTimeToAct(2, LastTimeToExit, ExitPair);
8100  }
8103  TrainController->OpTimeToActUpdateCounter = 1; //reset so it won't update for 5 seconds (not 0 so doesn't trigger a further update immediately)
8104  }
8105 
8106  ClearandRebuildRailway(6); // to replot new shorter route
8107  }
8109  TrainController->BaseTime = TDateTime::CurrentDateTime();
8111  Screen->Cursor = TCursor(-2); // Arrow
8112  }
8113 
8114  else // gap flashing, don't allow to interfere with RouteCancelFlag
8115  {
8116  TrainController->LogEvent("mbRight, GapFlashingInOperOrPreStartMode");
8117  int Position;
8118  TTrackElement TrackElement;
8119  if(Track->FindNonPlatformMatch(4, HLoc, VLoc, Position, TrackElement))
8120  {
8121  ;
8122  }
8123  {
8124  if((TrackElement.TrackType == GapJump) && (TrackElement.Conn[0] > -1))
8125  {
8126  if((TrackElement.TrainIDOnElement == -1) && (Track->TrackElementAt(818, TrackElement.Conn[0]).TrainIDOnElement == -1))
8127  {
8128  // don't flash if train on either gap element
8129  Track->GapFlashGreenPosition = TrackElement.Conn[0];
8134  Track->GapFlashRedPosition = Position;
8139  Track->GapFlashFlag = true;
8140  ClearandRebuildRailway(98); //added at v2.15.1 to clear any existing highlighted gaps
8141  }
8142  }
8143  }
8144  Utilities->CallLogPop(42);
8145  return; // covers above else & included here in case any more usermodes added later
8146  }
8147  }
8148  // deal with gap selection - if no other right button selection - apply for any mode (also included in OperMode above)
8149  TrainController->LogEvent("mbRight, GapFlashingNotOperOrPreStartMode");
8150  int Position;
8151  TTrackElement TrackElement;
8152  if(Track->FindNonPlatformMatch(18, HLoc, VLoc, Position, TrackElement))
8153  {
8154  ;
8155  }
8156  {
8157  if((TrackElement.TrackType == GapJump) && (TrackElement.Conn[0] > -1))
8158  {
8159  if((TrackElement.TrainIDOnElement == -1) && (Track->TrackElementAt(819, TrackElement.Conn[0]).TrainIDOnElement == -1))
8160  {
8161  // don't flash if train on either gap element
8162  Track->GapFlashGreenPosition = TrackElement.Conn[0];
8166  Track->GapFlashRedPosition = Position;
8170  Track->GapFlashFlag = true;
8171  ClearandRebuildRailway(97); //added at v2.15.1 to clear any existing highlighted gaps
8172  }
8173  }
8174  }
8175  Utilities->CallLogPop(67);
8176  return; // covers above else & included here in case any more usermodes added later
8177  }
8178 
8179 // Left Mouse Button Functions
8180  if(RouteCancelFlag)
8181  {
8183  }
8184  mbLeftDown = true;
8185 
8186  if(Level2TrackMode == AddTrack)
8187  {
8188  TrainController->LogEvent("mbLeft + AddTrack");
8189  Screen->Cursor = TCursor(-11); // Hourglass;
8191  bool TrackLinkingRequiredFlag;
8192  int CurrentTag;
8193  TSpeedButton *TempSpeedButton = 0;
8194  if(CurrentSpeedButton)
8195  {
8196  CurrentTag = CurrentSpeedButton->Tag;
8197  TempSpeedButton = CurrentSpeedButton;
8198  }
8199  else
8200  {
8201  CurrentTag = 0;
8202  }
8203  bool InternalChecks = true;
8204  bool PerformNameSearch = true;
8205  Track->PlotAndAddTrackElement(1, CurrentTag, 0, HLoc, VLoc, TrackLinkingRequiredFlag, InternalChecks, PerformNameSearch);
8206  // above now has extra zero 'Aspect' parameter at v2.2.0 so can distinguish between adding track and pasting
8207  EditMenu->Enabled = true;
8208  if(Track->NamedLocationElementAt(1, HLoc, VLoc))
8209  {
8210  ClearandRebuildRailway(7); // so named location graphics plotted correctly
8211  }
8212  if(TrackLinkingRequiredFlag)
8213  {
8214  Track->SetTrackFinished(false);
8215  }
8216  SetTrackBuildImages(10);
8217  SetGapsButton->Enabled = false; // if conditions have changed need to reset buttons, best not calling SetLevel2TrackMode as that
8218  TrackOKButton->Enabled = false; // calls Clearand.. if gridflag set & takes a long time
8219  if(Track->GapsUnset(2))
8220  {
8221  SetGapsButton->Enabled = true;
8222  }
8223  // only enable if there are gaps still to be set (returns false for no track)
8224  else
8225  {
8226  if(!(Track->NoActiveTrack(1)) && !(Track->IsTrackFinished()))
8227  {
8228  TrackOKButton->Enabled = true;
8229  }
8230  // TrackOK only enabled if track exists, there are no unset gaps, and track not finished
8231  }
8232  if(!(Track->IsTrackFinished())) // can only set lengths for several elements together if TrackFinished
8233  {
8234  SetLengthsButton->Enabled = false;
8235  }
8236  if(TempSpeedButton) // restore button if was pressed
8237  {
8238  CurrentSpeedButton = TempSpeedButton;
8239  CurrentSpeedButton->Down = true;
8240  }
8241  Screen->Cursor = TCursor(-2); // Arrow
8242  Utilities->CallLogPop(44);
8243  return;
8244  }
8245 
8246  else if(Level2TrackMode == AddGraphic)
8247  {
8248  TrainController->LogEvent("mbLeft + AddGraphic");
8249 // ResetChangedFileDataAndCaption(, false); //moved to later after 2.7.0 in case can't find it
8250  TUserGraphicItem NewGI;
8251  TTrack::TUserGraphicMap::iterator UGMIt = Track->UserGraphicMap.find(SelectedGraphicFileName);
8252  if(UGMIt != Track->UserGraphicMap.end()) // if it is the end then nothing was found
8253  {
8254  NewGI.UserGraphic = UGMIt->second;
8255  NewGI.Width = UGMIt->second->Width;
8256  NewGI.Height = UGMIt->second->Height;
8258  NewGI.HPos = X + (Display->DisplayOffsetH * 16);
8259  NewGI.VPos = Y + (Display->DisplayOffsetV * 16);
8260  Track->UserGraphicVector.push_back(NewGI);
8261  Display->PlotAndAddUserGraphic(1, NewGI);
8262  ResetChangedFileDataAndCaption(24, false); // moved to here after 2.7.0
8263  }
8264  else
8265  {
8266  ShowMessage("Unable to find graphic file " + SelectedGraphicFileName + ". Check it still exists.");
8267  Utilities->CallLogPop(2195);
8268  return;
8269  }
8270  MoveTextOrGraphicButton->Enabled = true;
8271  EditMenu->Enabled = true;
8272  Utilities->CallLogPop(2182);
8273  return;
8274  }
8275 
8276  else if(Level2TrackMode == AddLocationName)
8277  {
8278  TrainController->LogEvent("mbLeft + AddLocationName");
8279 // ResetChangedFileDataAndCaption(, true); moved after 2.7.0 to LocationNameKeyUp in case nothing changed
8280  bool FoundFlag;
8281  TTrackElement TrackElement;
8282  AnsiString NameString;
8283  TTrack::TIMPair InactivePair = Track->GetVectorPositionsFromInactiveTrackMap(1, HLoc, VLoc, FoundFlag);
8284  if(!FoundFlag)
8285  {
8286  Utilities->CallLogPop(45);
8287  return; // inactive element not found (has to be a platform or named non-station location, can't select any other element)
8288  }
8289  TTrackElement& InactiveTrackElement1 = Track->InactiveTrackElementAt(28, InactivePair.first);
8290  TTrackElement& InactiveTrackElement2 = Track->InactiveTrackElementAt(29, InactivePair.second); // may be same element if only 1
8291  TTrackElement& ValidElement = InactiveTrackElement1;
8292  unsigned int ValidPosition;
8293  if((InactiveTrackElement1.TrackType != Platform) && (InactiveTrackElement2.TrackType != Platform) &&
8294  (InactiveTrackElement1.TrackType != NamedNonStationLocation) && (InactiveTrackElement2.TrackType != NamedNonStationLocation) &&
8295  (InactiveTrackElement1.TrackType != Concourse) && (InactiveTrackElement2.TrackType != Concourse))
8296  {
8297  Utilities->CallLogPop(46);
8298  return; // element not valid
8299  }
8300  if((InactiveTrackElement1.TrackType == Platform) || (InactiveTrackElement1.TrackType == NamedNonStationLocation) ||
8301  (InactiveTrackElement1.TrackType == Concourse))
8302  {
8303  ValidElement = InactiveTrackElement1;
8304  ValidPosition = InactivePair.first;
8305  }
8306  else if((InactiveTrackElement2.TrackType == Platform) || (InactiveTrackElement2.TrackType == NamedNonStationLocation) ||
8307  (InactiveTrackElement2.TrackType == Concourse))
8308  {
8309  ValidElement = InactiveTrackElement2;
8310  ValidPosition = InactivePair.second;
8311  }
8312  // now have required element as ValidElement & position in InactiveTrackvector as ValidPosition
8313 
8314  // put a square box round element to show selection
8315  Display->Rectangle(0, HLoc * 16, VLoc * 16, clB0G0R5, 0, 2);
8316  LocationNameTextBox->Visible = true;
8317  LocationNameTextBox->SetFocus();
8318  NameString = Track->GetLocationName(ValidPosition);
8319  LocationNameTextBox->Text = NameString;
8320  InfoPanel->Visible = true;
8321  InfoPanel->Caption = "NAMING LOCATIONS: Enter name, 'Carriage Return' to accept, 'Escape' to quit";
8322 
8323  Track->LNPendingList.clear();
8324  Track->LNPendingList.insert(Track->LNPendingList.end(), ValidPosition);
8325  Level2TrackMode = NoTrackMode; // if leave as AddLocationName can select other squares before enter name
8326  Utilities->CallLogPop(47);
8327  return;
8328  }
8329 
8330  else if(Level2TrackMode == DistanceStart) // new for extended distances - similar to !PrefDirContinuing
8331  // prior to selecting start element
8332  {
8333  TrainController->LogEvent("mbLeft + DistanceStart");
8334 // ResetChangedFileDataAndCaption(, true); dropped after 2.7.0 in case only checking, moved to buttons in TracklengthPanel
8335  if(ConstructPrefDir->GetPrefDirStartElement(0, HLoc, VLoc))
8336  {
8339  SetLevel1Mode(65);
8341  SetLevel2TrackMode(30);
8342  }
8343  Utilities->CallLogPop(48);
8344  return;
8345  }
8346 
8347  else if(Level2TrackMode == DistanceContinuing) // new for extended distances - similar to PrefDirContinuing
8348  // prior to selecting finish element
8349  {
8350  TrainController->LogEvent("mbLeft + DistanceContinuing");
8351 // ResetChangedFileDataAndCaption(, true); dropped after 2.7.0 in case only checking, moved to buttons in TracklengthPanel
8352  bool FinishElement = false, LeadingPointsAtLastElement = false;
8353  Screen->Cursor = TCursor(-11); // Hourglass;
8354  if((ConstructPrefDir->PrefDirSize() != 1) || (ConstructPrefDir->GetFixedPrefDirElementAt(181, 0).HLoc != HLoc) ||
8355  (ConstructPrefDir->GetFixedPrefDirElementAt(182, 0).VLoc != VLoc))
8356  {
8357  // not same as start element
8358  if(ConstructPrefDir->GetNextPrefDirElement(0, HLoc, VLoc, FinishElement))
8359  {
8362  ConstructPrefDir->CalcDistanceAndSpeed(1, OverallDistance, OverallSpeedLimit, LeadingPointsAtLastElement);
8363  /*drop this condition as don't want to finish automatically because may have found an inappropriate path, changed at v2.13.0
8364  if(FinishElement)
8365  {
8366  TrackLengthPanel->Visible = true;
8367  TrackLengthPanel->SetFocus();
8368  InfoPanel->Visible = true;
8369  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Set values (overall length), or right click to cancel/truncate";
8370  RestoreAllDefaultLengthsButton->Enabled = true;
8371  ResetDefaultLengthButton->Enabled = true;
8372  LengthOKButton->Enabled = true;
8373  DistanceBox->Text = AnsiString(OverallDistance);
8374  if(OverallSpeedLimit > -1)
8375  {
8376  SpeedLimitBox->Text = AnsiString(OverallSpeedLimit);
8377  }
8378  else
8379  {
8380  SpeedLimitBox->Text = "Mixed";
8381  }
8382  ClearandRebuildRailway(52);
8383  Screen->Cursor = TCursor(-2); // Arrow
8384  Utilities->CallLogPop(1527);
8385  return;
8386  } */
8387 // else
8388  {
8389  if(!LeadingPointsAtLastElement)
8390  {
8391  TrackLengthPanel->Visible = true;
8392  TrackLengthPanel->SetFocus();
8393  InfoPanel->Visible = true;
8394  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Continue or set values (overall length), or right click to cancel/truncate";
8395  RestoreAllDefaultLengthsButton->Enabled = true;
8396  ResetDefaultLengthButton->Enabled = true;
8397  LengthOKButton->Enabled = true;
8398  DistanceBox->Text = AnsiString(OverallDistance);
8399  if(OverallSpeedLimit > -1)
8400  {
8401  SpeedLimitBox->Text = AnsiString(OverallSpeedLimit);
8402  }
8403  else
8404  {
8405  SpeedLimitBox->Text = "Mixed";
8406  }
8407  // Level2TrackMode = DistanceContinuing;
8408  // SetLevel2TrackMode();
8409  }
8410  else
8411  {
8412  TrackLengthPanel->Visible = true;
8413  TrackLengthPanel->SetFocus();
8414  InfoPanel->Visible = true;
8415  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Can't end on leading points, need to continue or truncate";
8416  RestoreAllDefaultLengthsButton->Enabled = false;
8417  ResetDefaultLengthButton->Enabled = false;
8418  LengthOKButton->Enabled = false;
8419  // Level2TrackMode = DistanceContinuing;
8420  // SetLevel2TrackMode();
8421  }
8422  }
8423  }
8424  }
8425  else // same as start element
8426  {
8429  SetLevel2TrackMode(54);
8430  Screen->Cursor = TCursor(-2); // Arrow
8431  Utilities->CallLogPop(1713);
8432  return;
8433  }
8435  Screen->Cursor = TCursor(-2); // Arrow
8436  Utilities->CallLogPop(1490);
8437  return;
8438  }
8439 
8440  else if(Level2TrackMode == GapSetting)
8441  {
8442  TrainController->LogEvent("mbLeft + GapSetting");
8443 // ResetChangedFileDataAndCaption(, true); moved to later after 2.7.0 in case can't set it
8444  // HighLightOneGap already called once from SetLevel2TrackMode so have all gap element values set
8445  // & it is highlighted
8446  if(!(Track->FindSetAndDisplayMatchingGap(1, HLoc, VLoc)))
8447  {
8448  Utilities->CallLogPop(50);
8449  return; // true if finds one
8450  }
8451  ResetChangedFileDataAndCaption(11, true); // moved to here after 2.7.0 in case can't set it
8452  InfoPanel->Visible = true;
8453  InfoPanel->Caption = "CONNECTING GAPS: Connecting element selected";
8454  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
8455  Delay(0, 500); // 500 msec delay before next selection requested
8456 
8457  // ClearandRebuildRailway(8);//get rid of gap selections
8458  // need to call this later when new gap displayed, else old gap remains
8459 
8460  // now back to highlighting next gap
8461  // bool LocError = false;
8462  if(!(HighLightOneGap(1, HLoc, VLoc)))
8463  {
8464  // all gaps set
8465  ShowMessage("All gaps set");
8466  if(Level2TrackMode == AddTrack)
8467  {
8469  SetLevel1Mode(66);
8470  SetLevel2TrackMode(31);
8471  }
8472  else
8473  {
8475  SetLevel1Mode(37);
8476  }
8477  ClearandRebuildRailway(9); // get rid of last gap ellipse
8478  Utilities->CallLogPop(51);
8479  return;
8480  }
8481  // here if one gap highlighted so return to user to allow corresponding gap to be selected
8482  // by another call to MainScreenMouseDown
8483  }
8484 
8485  else if(Level2TrackMode == AddText)
8486  {
8487  TrainController->LogEvent("mbLeft + AddText");
8488 // ResetChangedFileDataAndCaption(, true); moved after 2.7.0 to TextBoxKeyPress in case nothing changed
8489  // X & Y are relative to Display output, but TextBox is placed relative to Form
8490  // if mouse position on first character of an existing piece of text reload it into the editor
8491 
8492  bool TextFoundFlag = false;
8493  int TrueX = 0, TrueY = 0;
8494  AnsiString ExistingText = "";
8496  TFont *ExistingTextFont = new TFont;
8497  int ExistingTextHPos = 0, ExistingTextVPos = 0;
8498  Track->GetTruePositionsFromScreenPos(1, TrueX, TrueY, X, Y);
8499  if(!TextHandler->TextVector.empty())
8500  {
8501  for(TextPtr = (TextHandler->TextVector.end() - 1); TextPtr >= TextHandler->TextVector.begin(); TextPtr--)
8502  {
8503  if((TrueX >= TextPtr->HPos) && (TrueX < (TextPtr->HPos + abs(TextPtr->Font->Height))) && (TrueY >= TextPtr->VPos) && (TrueY <
8504  (TextPtr->VPos + abs(TextPtr->Font->Height))))
8505  {
8506  ExistingText = TextPtr->TextString;
8507  ExistingTextFont->Assign(TextPtr->Font);
8508  ExistingTextHPos = TextPtr->HPos;
8509  ExistingTextVPos = TextPtr->VPos;
8510  TextFoundFlag = true;
8511  TextHandler->TextErase(9, TrueX, TrueY, ExistingText);
8512  break;
8513  } // if ....
8514 
8515  } // for TextPtr...
8516  } // if !TextVector...
8517 
8518  if(TextFoundFlag)
8519  {
8520  TextBox->Left = ExistingTextHPos + Display->Left() - (Display->DisplayOffsetH * 16) - 3;
8521  TextBox->Top = ExistingTextVPos + Display->Top() - (Display->DisplayOffsetV * 16) - 3;
8522  TextBox->Font->Assign(ExistingTextFont);
8523  Display->SetFont(ExistingTextFont);
8524  Text_X = ExistingTextHPos;
8525  Text_Y = ExistingTextVPos;
8526  }
8527  else
8528  {
8529  TextBox->Left = (TextOrUserGraphicGridVal * div((Display->Left() + X), TextOrUserGraphicGridVal).quot) - 3;
8530  TextBox->Top = (TextOrUserGraphicGridVal * div((Display->Top() + Y), TextOrUserGraphicGridVal).quot) - 3;
8531  TextBox->Font->Assign(Display->GetFont());
8532  Text_X = TextOrUserGraphicGridVal * div(NoOffsetX, TextOrUserGraphicGridVal).quot;
8533  Text_Y = TextOrUserGraphicGridVal * div(NoOffsetY, TextOrUserGraphicGridVal).quot;
8534  }
8535  TextBox->Visible = true;
8536  TextBox->SetFocus();
8537  if(TextFoundFlag)
8538  {
8539  TextBox->Text = ExistingText;
8540  }
8541  else
8542  {
8543  TextBox->Text = "New Text: CR=end, ESC=quit";
8544  }
8545  TextBox->Width = (abs(TextBox->Font->Height) * TextBox->Text.Length() * 0.7);
8546  TextBox->SelectAll();
8547  delete ExistingTextFont;
8548  ClearandRebuildRailway(29); // to remove old text if replaced
8550  Utilities->CallLogPop(1775);
8551  return; // If text input go no further
8552  }
8553 
8555  {
8556  TrainController->LogEvent("mbLeft + MoveTextOrGraphic");
8557 // ResetChangedFileDataAndCaption(, true); moved after 2.7.0 to later in case nothing found
8558  // int HPosInput;// = X + (Display->DisplayOffsetH * 16);
8559  // int VPosInput;// = Y + (Display->DisplayOffsetV * 16);
8560  // Track->GetTruePositionsFromScreenPos(HPosInput, VPosInput, X, Y);
8561  // StartX = X + (Display->DisplayOffsetH * 16);
8562  // StartY = Y + (Display->DisplayOffsetV * 16);
8565  if(!TextFoundFlag) // give precedence to text
8566  {
8569  {
8570  ResetChangedFileDataAndCaption(13, true); // moved here after 2.7.0 to save only if something found
8571  }
8572  }
8573  else
8574  {
8575  ResetChangedFileDataAndCaption(27, true); // and here
8576  }
8577  Utilities->CallLogPop(53);
8578  return; // if text move selected don't permit anything else
8579  }
8580 
8581  else if(Level2TrackMode == TrackSelecting)
8582  /* When 'select' chosen from the Edit menu (only available in 'AddTrack') conditions are set ready to enclose a rectangular screen area
8583  using MouseMove. When MouseDown occurs the starting point is marked (wrt whole railway, not just the screen) and stored in
8584  SelectStartPair. If the mouse button is released and a new start position selected then the earlier one is discarded. Providing the
8585  button is held down subsequent actions occur during MouseMove (to display the changing rectangle) and MouseUp to define the final
8586  selected rectangle.
8587  */
8588  {
8589  TrainController->LogEvent("mbLeft + TrackSelecting");
8590  ClearandRebuildRailway(10); // to get rid of earlier rectangles
8591  SelectStartPair.first = HLoc;
8592  SelectStartPair.second = VLoc;
8593  }
8594 
8595  else if((Level2TrackMode == CopyMoving) || (Level2TrackMode == CutMoving))
8596  /* The same actions apply on MouseDown whether Copy or Cut selected from the menu. First the horizontal and vertical mouse position is
8597  checked and unless it lies within the selected rectangle and not within 4 pixels of an edge the pickup fails and the function returns.
8598  Otherwise flag SelectPickedUp is set to true (to allow it to move during MouseMove and remain in place at MouseUp) and the mouse position
8599  is saved in SelectBitmapMouseLocX & Y for use later in MouseMove & MouseUp.
8600  */
8601  {
8602  TrainController->LogEvent("mbLeft + CopyMoving or CutMoving");
8603 // ResetChangedFileDataAndCaption(, true); moved after 2.7.0 to later in case don't proceed
8604  if((X < ((SelectBitmapHLoc - Display->DisplayOffsetH) * 16) + 4) ||
8605  (X > ((SelectBitmapHLoc + (SelectBitmap->Width / 16) - Display->DisplayOffsetH) * 16) - 4))
8606  {
8607  SelectPickedUp = false;
8608  Utilities->CallLogPop(54);
8609  return; // within 4 pixels of outside of horizontal area (4 pixels are so can't push selection off edge of screen)
8610  }
8611  if((Y < ((SelectBitmapVLoc - Display->DisplayOffsetV) * 16) + 4) ||
8612  (Y > ((SelectBitmapVLoc + (SelectBitmap->Height / 16) - Display->DisplayOffsetV) * 16) - 4))
8613  {
8614  SelectPickedUp = false;
8615  Utilities->CallLogPop(55);
8616  return; // within 4 pixels of outside of vertical area (4 pixels are so can't push selection off edge of screen)
8617  }
8618  else
8619  {
8620  SelectPickedUp = true;
8621  }
8622  ResetChangedFileDataAndCaption(14, true); // moved here after 2.7.0 in case don't proceed
8625  }
8626 
8628  {
8629  TrainController->LogEvent("mbLeft + != PrefDirContinuing");
8630 // ResetChangedFileDataAndCaption(, false); //moved after 2.7.0 to later in case don't click on element
8631 // RlyFile = false; - don't alter this just for PrefDir changes
8632  if(ConstructPrefDir->GetPrefDirStartElement(1, HLoc, VLoc))
8633  {
8634  ResetChangedFileDataAndCaption(15, false); // moved after 2.7.0 to here
8638  }
8639  Utilities->CallLogPop(56);
8640  return;
8641  }
8642 
8644  {
8645  TrainController->LogEvent("mbLeft + PrefDirContinuing");
8646 // ResetChangedFileDataAndCaption(, false); //moved after 2.7.0 to later in case don't click on element
8647 // RlyFile = false; - don't alter this just for PrefDir changes
8648  bool FinishElement;
8649  Screen->Cursor = TCursor(-11); // Hourglass;
8650  if((ConstructPrefDir->PrefDirSize() != 1) || (ConstructPrefDir->GetFixedPrefDirElementAt(183, 0).HLoc != HLoc) ||
8651  (ConstructPrefDir->GetFixedPrefDirElementAt(184, 0).VLoc != VLoc))
8652  {
8653  // not same as start element
8654  if(ConstructPrefDir->GetNextPrefDirElement(1, HLoc, VLoc, FinishElement))
8655  {
8657  ResetChangedFileDataAndCaption(16, false); // moved after 2.7.0 to here
8658  /*drop this condisiton as don't want to finish automatically because may have found an inappropriate path, changed at v2.13.0
8659  {
8660  ShowMessage("Preferred direction added");
8661  EveryPrefDir->ConsolidatePrefDirs(1, ConstructPrefDir); // at 31
8662  Level1Mode = PrefDirMode;
8663  SetLevel1Mode(16);
8664  Screen->Cursor = TCursor(-2); // Arrow
8665  Utilities->CallLogPop(57);
8666  return;
8667  }
8668  */
8669 // else
8670  {
8673  }
8674  // set again since 1st time
8675  // PrefDir vector only had start element & Truncate wasn't enabled, also need
8676  // to do the checks for Loop & End for each element as it is added
8677  }
8678  }
8679  else // same as start element
8680  {
8683  SetLevel1Mode(121);
8684  Screen->Cursor = TCursor(-2); // Arrow
8685  Utilities->CallLogPop(1714);
8686  return;
8687  }
8688  Screen->Cursor = TCursor(-2); // Arrow
8689  Utilities->CallLogPop(58);
8690  return;
8691  }
8692 
8694  {
8695  TrainController->LogEvent("mbLeft + PrefDirSelecting");
8696  ClearandRebuildRailway(56); // to get rid of earlier rectangles
8697  SelectStartPair.first = HLoc;
8698  SelectStartPair.second = VLoc;
8699  }
8700 
8701  else if(Level1Mode == OperMode)
8702  {
8703  if((Level2OperMode == Operating) && CallingOnButton->Down && CallingOnButton->Enabled)
8704  {
8705  TrainController->LogEvent("mbLeft + Operating & CallingOnButton->Down");
8706  int Position;
8707  TTrackElement TrackElement;
8708  if(Track->FindNonPlatformMatch(2, HLoc, VLoc, Position, TrackElement))
8709  {
8710  if(TrackElement.TrackType != SignalPost)
8711  {
8712  CallingOnButton->Down = false;
8713 // InfoPanel->Visible = false; //dropped at v1.3.0, not sure what purpose intended to serve but don't want to lose the info panel as did with this here also added line below to reset
8715  Utilities->CallLogPop(59);
8716  return;
8717  }
8718  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
8719  {
8721  {
8723  x).LeadExitPos] == Position) && (TrackElement.Config[Track->TrackElementAt(429, TrainController->TrainVectorAt(28,
8725  {
8726  // found it!
8727 /*
8728  if(TrackElement.SpeedTag == 68)
8729  {
8730  Display->PlotOutput(0, (HLoc * 16), (VLoc * 16), RailGraphics->bm68CallingOn);
8731  }
8732  if(TrackElement.SpeedTag == 69)
8733  {
8734  Display->PlotOutput(1, (HLoc * 16), (VLoc * 16), RailGraphics->bm69CallingOn);
8735  }
8736  if(TrackElement.SpeedTag == 70)
8737  {
8738  Display->PlotOutput(2, (HLoc * 16), (VLoc * 16), RailGraphics->bm70CallingOn);
8739  }
8740  if(TrackElement.SpeedTag == 71)
8741  {
8742  Display->PlotOutput(3, (HLoc * 16), (VLoc * 16), RailGraphics->bm71CallingOn);
8743  }
8744  if(TrackElement.SpeedTag == 72)
8745  {
8746  Display->PlotOutput(4, (HLoc * 16), (VLoc * 16), RailGraphics->bm72CallingOn);
8747  }
8748  if(TrackElement.SpeedTag == 73)
8749  {
8750  Display->PlotOutput(5, (HLoc * 16), (VLoc * 16), RailGraphics->bm73CallingOn);
8751  }
8752  if(TrackElement.SpeedTag == 74)
8753  {
8754  Display->PlotOutput(6, (HLoc * 16), (VLoc * 16), RailGraphics->bm74CallingOn);
8755  }
8756  if(TrackElement.SpeedTag == 75)
8757  {
8758  Display->PlotOutput(7, (HLoc * 16), (VLoc * 16), RailGraphics->bm75CallingOn);
8759  }
8760 */
8761  Track->TrackElementAt(430, Position).CallingOnSet = true;
8762  Track->PlotSignal(13, Track->TrackElementAt(893, Position), Display);
8763 // added at v 1.3.0 in place of the above to ensure ground signals (as well as others) plot correctly for proceed
8764  // have to call after CallingOnSet becomes true & can't use TrackElement as that still has CallingOnSet false
8765  ClearandRebuildRailway(69); // added at v1.3.0 to replot route on element after PlotSignal above
8768 
8769  //below addition same as for adding and cancelling a route
8770  THVShortPair ExitPair; //added at v2.19.0 to update actions due panel immediately after new route set
8771  if((ActionsDueForm->Visible) && (Level2OperMode == Operating)) // ditto
8772  {
8773  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
8774  {
8775  float LastTimeToExit = TrainController->TrainVectorAt(96, x).TimeToExit;
8776  TrainController->TrainVectorAt(94, x).UpdateTrain(2); //included so 'StoppedAtSignal' flag dropped when route set ahead otherwise
8777  //the 'NOW' entries have to await the next OpTimeToActUpdateCounter update
8778  TrainController->TrainVectorAt(95, x).OpTimeToAct = TrainController->TrainVectorAt(101, x).CalcTimeToAct(3, LastTimeToExit, ExitPair);
8779  }
8782  TrainController->OpTimeToActUpdateCounter = 1; //reset so it won't update for 5 seconds (not 0 so doesn't trigger a further update immediately)
8783  }
8784 
8785  CallingOnButton->Down = false;
8787 
8788 // set an unrestricted route into the station (just to the first platform) from the stop signal (note that it may be last in an autosigs
8789 // route) but remove any single route elements first (can't reach here if constructing a route), else may try to extend a route that
8790 // has been removed (only a precaution, shouldn't cause any probs whether single route set or not)
8791  for(unsigned int x = 0; x < AllRoutes->AllRoutesSize(); x++)
8792  {
8793  if(AllRoutes->GetFixedRouteAt(192, x).PrefDirSize() == 1)
8794  {
8795  // only allow route element to be removed if not selected for a route start otherwise
8796  // StartSelectionRouteID will be set & will fail at convert
8798  {
8800  AllRoutes->RemoveRouteElement(21, PDE.HLoc, PDE.VLoc, PDE.GetELink());
8801  TrainController->LogEvent("SingleRouteElementRemovedDuringCallon, H = " + AnsiString(PDE.HLoc) + ", V = " +
8802  AnsiString(PDE.VLoc));
8803  }
8804  }
8805  }
8806 
8807 // find the correct entry in CallonVector - i.e. where Position == RouteStartEntry
8808  for(unsigned int x = 0; x < AllRoutes->CallonVector.size(); x++)
8809  {
8810  if(AllRoutes->CallonVector.at(x).RouteStartPosition == Position)
8811  {
8812  // found it
8813  if(!(AllRoutes->CallonVector.at(x).RouteOrPartRouteSet))
8814  // if RouteOrPartRouteSet false then set an unrestricted route into platform
8815  {
8816  bool PointsChanged = false;
8817  IDInt ReqPosRouteID(-1);
8818  TOneRoute *NewRoute = new TOneRoute;
8819  bool CallonTrue = true;
8820  if(NewRoute->GetNonPreferredRouteStartElement(1,
8821  Track->TrackElementAt(841, AllRoutes->CallonVector.at(x).RouteStartPosition).HLoc,
8822  Track->TrackElementAt(842, AllRoutes->CallonVector.at(x).RouteStartPosition).VLoc, CallonTrue))
8823  {
8824  if(NewRoute->GetNextNonPreferredRouteElement(1,
8825  Track->TrackElementAt(843, AllRoutes->CallonVector.at(x).PlatformPosition).HLoc,
8826  Track->TrackElementAt(844, AllRoutes->CallonVector.at(x).PlatformPosition).VLoc, CallonTrue,
8827  ReqPosRouteID, PointsChanged))
8828  {
8829  if(!PointsChanged) // shouldn't be changed, something wrong if true so don't plot route
8830  {
8831  NewRoute->ConvertAndAddNonPreferredRouteSearchVector(3, ReqPosRouteID);
8832  ClearandRebuildRailway(67); // to plot the route (only finds one so won't call repeatedly)
8833  }
8834  }
8835  }
8836  delete NewRoute;
8837  }
8838  }
8839  }
8840 // InfoPanel->Visible = false;
8841  Utilities->CallLogPop(60);
8842  return;
8843  }
8844  }
8845  }
8846  }
8847  CallingOnButton->Down = false;
8849  Utilities->CallLogPop(61);
8850  return;
8851  }
8852 /* get 1st element, first check if selected points, not in existing route, & in RouteNotStarted mode
8853  if so, set all the flash values, Track->PointFlashFlag & start time, then exit for flasher to take over.
8854  If any of above conditions not met then treat as route selection, setting route flasher if
8855  route continuing.
8856 */
8857 
8858  if((Level2OperMode == Operating) || (Level2OperMode == PreStart)) // not 'else if' as both may apply
8859  // disallow route setting if paused
8860  {
8861  if(Level2OperMode == PreStart)
8862  {
8863  PointsFlashDuration = 0.0;
8866  }
8867  else
8868  {
8869  float TempSpeedVal = 1; // added for v2.3.0 to keep durations same as x1 values for slow speeds
8870  if(TTClockSpeed < 1)
8871  {
8872  TempSpeedVal = TTClockSpeed;
8873  }
8874  PointsFlashDuration = AllRoutes->PointsDelay * TempSpeedVal;
8877  }
8878  if(RouteMode == RouteNotStarted)
8879  {
8880  TrainController->LogEvent("mbLeft + RouteNotStarted");
8881  int Position;
8882  TTrackElement TrackElement;
8883  if(Track->FindNonPlatformMatch(3, HLoc, VLoc, Position, TrackElement))
8884  {
8885  if((TrackElement.TrackType == Points) && !(AllRoutes->TrackIsInARoute(1, Position, 0))
8887  // Flash selected points & changeover if appropriate
8888  // need !Track->PointFlashFlag to prevent another point being selected while another is flashing, & !Track->RouteFlashFlag
8889  // to ensure user only does one thing at a time
8890  {
8891  if(TrackElement.TrainIDOnElement > -1)
8892  {
8893  TrainController->StopTTClockMessage(1, "Can't change points under a train!");
8894  Utilities->CallLogPop(62);
8895  return;
8896  }
8897  PointFlash->SetScreenHVSource(1, TrackElement.HLoc * 16, TrackElement.VLoc * 16);
8898 /*
8899  This used to try to allow any linked trailing edges to cause both points to change, but no good if
8900  there are two adjacent crossovers, where both trailing edges are linked to two different points.
8901  The wrong link might be chosen. Also doubtful if applying a strict order of checks would work, since
8902  may be obscure configurations that would be wrong. This function bypasses the MatchingPoint check, which
8903  ensures that there are no obscure links. Hence better to stick with original.
8904 
8905  //check if trailing edge linked to another point trailing edge
8906  int DivergingPosition = TrackElement.Conn[1];
8907  TTrackElement DivergingElement = Track->TrackElementAt(431, TrackElement.Conn[1]);
8908  DivergingPointVectorPosition = -1;
8909  if((DivergingElement.TrackType == Points) &&
8910  ((DivergingElement.Conn[1] == Position) || (DivergingElement.Conn[3] == Position)))
8911  {
8912  if(AllRoutes->TrackIsInARoute(, DivergingPosition))
8913  {
8914  ShowMessage("Linked points Locked");
8915  }
8916  else DivergingPointVectorPosition = DivergingPosition;
8917  }
8918  else
8919  {
8920  DivergingPosition = TrackElement.Conn[3];
8921  DivergingElement = Track->TrackElementAt(432, TrackElement.Conn[3]);
8922  if((DivergingElement.TrackType == Points) &&
8923  ((DivergingElement.Conn[1] == Position) || (DivergingElement.Conn[3] == Position)))
8924  {
8925  if(AllRoutes->TrackIsInARoute(, DivergingPosition))
8926  {
8927  ShowMessage("Linked points locked");
8928  }
8929  else DivergingPointVectorPosition = DivergingPosition;
8930  }
8931  }
8932  Track->PointFlashFlag = true;
8933  PointFlashVectorPosition = Position;
8934  PointFlashStartTime = TrainController->TTClockTime;
8935  [close curly bracket - if include it matches earlier non-commented one!]
8936 */
8937  TTrackElement DivergingElement = Track->TrackElementAt(433, TrackElement.Conn[3]);
8938  int DivergingPosition = TrackElement.Conn[3];
8939  if((DivergingElement.TrackType == Points) && (DivergingElement.Conn[3] == Position) && (Track->MatchingPoint(1, Position,
8940  DivergingPosition))) // full match inc same attributes
8941  {
8942  if(AllRoutes->TrackIsInARoute(4, DivergingPosition, 0))
8943  {
8944  TrainController->StopTTClockMessage(2, "Linked points locked");
8945  }
8946  else if(Track->TrackElementAt(1506, Position).Failed) //this point failed
8947  {
8948  TrainController->StopTTClockMessage(124, "Points failed, unable to change");
8949  }
8950  else //this point may still fail
8951  {
8952  //not failed when mouse down, but either may fail in trying to change
8953  {
8954  bool SkipFlashing = false;
8955  if(Utilities->FailureMode != FNil)
8956  {
8957  if(random(Utilities->PointChangeEventsPerFailure) == 0)
8958  {
8960  TTrackElement &TE = Track->TrackElementAt(1507, Position); //this point
8961  IFE.TVPos = Position;
8962  TE.Failed = true; //new value to signify failed & can't be changed (keeps original value as last bit)
8963  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = TE.SpeedLimit01; //store these values temporarily, points aren't bridges so can use these
8965  TE.SpeedLimit01 = 10; //values while failed
8966  TE.SpeedLimit23 = 10;
8967  Display->WarningLog(16, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Points failed at " + TE.ElementID);
8968  PerfLogForm->PerformanceLog(39, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Points failed at " + TE.ElementID);
8969  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
8970  //set repair time, random value in minutes between 10 and 179
8971  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime);
8972  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
8973  IFE.RepairTime = RepairTime;
8975  Track->FailedPointsVector.push_back(IFE);
8976  SkipFlashing = true;
8977  TrainController->StopTTClockMessage(125, "Points at " + TE.ElementID + " failed during an attempt to change manually.");
8978  }
8979  if(random(Utilities->PointChangeEventsPerFailure) == 0)
8980  {
8982  TTrackElement &TE = Track->TrackElementAt(1508, DivergingPosition); //diverging point
8983  if(!TE.Failed) //can't fail twice
8984  {
8985  IFE.TVPos = DivergingPosition;
8986  TE.Failed = true; //new value to signify failed & can't be changed (keeps original value as last bit)
8987  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = TE.SpeedLimit01; //store these values temporarily, points aren't bridges so can use these
8989  TE.SpeedLimit01 = 10; //values while failed
8990  TE.SpeedLimit23 = 10;
8991  Display->WarningLog(17, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Points failed at " + TE.ElementID);
8992  PerfLogForm->PerformanceLog(40, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Points failed at " + TE.ElementID);
8993  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
8994  //set repair time, random value in minutes between 10 and 179
8995  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime);
8996  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
8997  IFE.RepairTime = RepairTime;
8999  Track->FailedPointsVector.push_back(IFE);
9000  SkipFlashing = true;
9001  TrainController->StopTTClockMessage(126, "Points at " + TE.ElementID + " failed during an attempt to change manually.");
9002  }
9003  }
9004  }
9005  if(!SkipFlashing)
9006  {
9007  Track->PointFlashFlag = true;
9008  PointFlashVectorPosition = Position;
9009  if(!Track->TrackElementAt(1509, DivergingPosition).Failed) //don't change if failed
9010  {
9011  DivergingPointVectorPosition = DivergingPosition;
9012  }
9014  }
9015  }
9016  }
9017  }
9018  else // no matching point, just change this point, but it might fail
9019  {
9020  if(Track->TrackElementAt(1510, Position).Failed) //this point failed
9021  {
9022  TrainController->StopTTClockMessage(127, "Points failed, unable to change");
9023  }
9024  else
9025  {
9026  bool SkipFlashing = false;
9027  if(Utilities->FailureMode != FNil)
9028  {
9029  if(random(Utilities->PointChangeEventsPerFailure) == 0)
9030  {
9032  TTrackElement &TE = Track->TrackElementAt(1511, Position); //this point
9033  IFE.TVPos = Position;
9034  TE.Failed = true; //new value to signify failed & can't be changed (keeps original value as last bit)
9035  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = TE.SpeedLimit01; //store these values temporarily, points aren't bridges so can use these
9037  TE.SpeedLimit01 = 10; //values while failed
9038  TE.SpeedLimit23 = 10;
9039  Display->WarningLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Points failed at " + TE.ElementID);
9040  PerfLogForm->PerformanceLog(41, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Points failed at " + TE.ElementID);
9041  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
9042  //set repair time, random value in minutes between 10 and 179
9043  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime);
9044  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
9045  IFE.RepairTime = RepairTime;
9047  Track->FailedPointsVector.push_back(IFE);
9048  TrainController->StopTTClockMessage(128, "Points at " + TE.ElementID + " failed during an attempt to change manually.");
9049  SkipFlashing = true;
9050  }
9051  }
9052  if(!SkipFlashing)
9053  {
9054  Track->PointFlashFlag = true;
9055  PointFlashVectorPosition = Position;
9058  }
9059  }
9060  }
9061  }
9062 
9063  else if(Track->IsLCAtHV(59, HLoc, VLoc) && !Track->PointFlashFlag && !Track->RouteFlashFlag)
9064  // level crossing added at v2.6.0 to allow manual LC changing
9065  {
9066  if(Track->GetInactiveTrackElementFromTrackMap(5, HLoc, VLoc).Attribute != 2) // 2 = LC changing state, can't click if changing
9067  {
9068  Track->LCChangeFlag = true;
9069  bool TrainPresent = false;
9070  if(Track->IsLCBarrierDownAtHV(4, HLoc, VLoc)) // if true then may be able to raise barriers
9071  {
9072  // first need to identify the LC in the BarriersDownVector
9073  int BDVectorPos = -1;
9074  if(Track->AnyLinkedBarrierDownVectorManual(1, HLoc, VLoc, BDVectorPos)) // looking for same position & manually closed
9075  {
9076  // this largely copied from ClockTimer2
9078  Track->BarriersDownVector.at(BDVectorPos).VLoc, ConstructRoute->SearchVector, TrainPresent))
9079  // returns true for route set or being set or train, and TrainPresent true if train on LC
9080  {
9081  TTrack::TActiveLevelCrossing CLC = Track->BarriersDownVector.at(BDVectorPos);
9082  // check if have exceeded the allowance (3 minutes for a train having passed or 0 for not) and add it to the overall excess time
9083  TDateTime TempExcessLCDownTime;
9084  if(Track->BarriersDownVector.at(BDVectorPos).ReducedTimePenalty)
9085  // this set in ClockTimer2, relies on train being on LC for >= 1 second
9086  {
9087  // get the 3 mins allowance - hard to imagine will pass in less than a second!
9088  TempExcessLCDownTime = TrainController->TTClockTime - CLC.StartTime - TDateTime(180.0 / 86400);
9089  }
9090  else
9091  {
9092  TempExcessLCDownTime = TrainController->TTClockTime - CLC.StartTime;
9093  }
9094  if(TempExcessLCDownTime > TDateTime(0))
9095  {
9096  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
9097  }
9098  CLC.StartTime = TrainController->TTClockTime; // reset these 3 members
9101  Track->SetLinkedLevelCrossingBarrierAttributes(7, CLC.HLoc, CLC.VLoc, 2); // set attr to 2 for changing state
9102  Track->ChangingLCVector.push_back(CLC);
9103  Track->BarriersDownVector.erase(Track->BarriersDownVector.begin() + BDVectorPos);
9104  }
9105  }
9106  }
9107  else // lowering
9108  {
9109  // this largely copied from SetLCChangeValues
9110  TTrack::TActiveLevelCrossing ALC; // constructor sets ReducedTimePenalty to false
9111  ALC.HLoc = HLoc;
9112  ALC.VLoc = VLoc;
9114  ALC.BaseElementSpeedTag = TrackElement.SpeedTag;
9117  ALC.TypeOfRoute = 2;
9118  Track->SetLinkedManualLCs(0, HLoc, VLoc);
9119 // this sets all linked LC ConsecSignals values to 2 for manually lowered - differs from SetLCChangeValues which uses the route type
9120  Track->SetLinkedLevelCrossingBarrierAttributes(6, HLoc, VLoc, 2); // set attr to 2 for changing state
9121  Track->ChangingLCVector.push_back(ALC);
9123  {
9124  AnsiString Message =
9125  AnsiString("This will open the level crossing manually (it will show in green).\n\nA manually opened"
9126  " level crossing must be manually closed, and as soon as possible to avoid time penalties.\n\n" "This message will not be shown again."
9127  );
9128  TrainController->StopTTClockMessage(93, Message);
9130  }
9131  }
9132  }
9133  }
9134  else // route start
9135  {
9136  if(AutoSigsFlag)
9137  {
9138  AutoRouteStartMarker->SetScreenHVSource(2, TrackElement.HLoc * 16, TrackElement.VLoc * 16);
9140  }
9141  else if(PreferredRoute) // added at v2.7.0, was ConsecSignalsRoute
9142  {
9143  SigRouteStartMarker->SetScreenHVSource(3, TrackElement.HLoc * 16, TrackElement.VLoc * 16);
9145  }
9146  else
9147  {
9148  NonSigRouteStartMarker->SetScreenHVSource(4, TrackElement.HLoc * 16, TrackElement.VLoc * 16);
9150  }
9151  if(PreferredRoute)
9152  {
9153  if(!Track->PointFlashFlag && !Track->RouteFlashFlag) // don't allow a route to start if a point changing or
9154  // another route building
9155  {
9156  ConstructRoute->ClearRoute(); // in case not empty though should be
9158  {
9159  if(AutoSigsFlag)
9160  {
9162  }
9163  else
9164  {
9166  }
9168  InfoPanel->Visible = true;
9169  if(Level2OperMode == PreStart)
9170  {
9171  InfoPanel->Caption = "PRE-START: Select next route location";
9172  }
9173  else
9174  {
9175  InfoPanel->Caption = "OPERATING: Select next route location";
9176  }
9177  }
9178  }
9179  Utilities->CallLogPop(63);
9180  return;
9181  }
9182  else // nonpreferred route
9183  {
9184  if(!Track->PointFlashFlag && !Track->RouteFlashFlag) // don't allow a route to start if a point changing or
9185  // another route building
9186  {
9187  ConstructRoute->ClearRoute(); // in case not empty though should be
9188  bool CallonFalse = false;
9189  if(ConstructRoute->GetNonPreferredRouteStartElement(0, HLoc, VLoc, CallonFalse))
9190  {
9193  InfoPanel->Visible = true;
9194  if(Level2OperMode == PreStart)
9195  {
9196  InfoPanel->Caption = "PRE-START: Select next route location";
9197  }
9198  else
9199  {
9200  InfoPanel->Caption = "OPERATING: Select next route location";
9201  }
9202  }
9203  }
9204  Utilities->CallLogPop(64);
9205  return;
9206  } // NonPreferred route
9207 
9208  } // TrackType != Points
9209 
9210  } // if(Track->FindNonPlatformMatch(HLoc, VLoc, Position, TrackElement))
9211 
9212  } // if(RouteMode == RouteNotStarted)
9213  else // RouteContinuing
9214  {
9215  TrainController->LogEvent("mbLeft + RouteContinuing");
9216  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
9219  AutoRouteStartMarker->PlotOriginal(14, Display); // if overlay not plotted will ignore
9220  SigRouteStartMarker->PlotOriginal(15, Display); // if overlay not plotted will ignore
9221  NonSigRouteStartMarker->PlotOriginal(16, Display); // if overlay not plotted will ignore
9222  Screen->Cursor = TCursor(-11); // Hourglass - also set to an hourglass when flashing, after found required
9223  // element, but this sets it to an hourglass while searching
9224  bool PointsChanged = false;
9225  if(PreferredRoute)
9226  {
9227  // route added to AllRoutes in GetNextRouteElement if valid
9228  // int ReqPosRouteNumber;
9230  ConstructRoute->ReqPosRouteID, PointsChanged))
9231  {
9232  Track->RouteFlashFlag = true;
9233  PreferredRouteFlag = true;
9234  float TempSpeedVal = 1; // added for v2.3.0 to keep durations same as x1 values for slow speeds
9235  if(TTClockSpeed < 1)
9236  {
9237  TempSpeedVal = TTClockSpeed;
9238  }
9239  if(Level2OperMode == PreStart)
9240  {
9241  RouteFlashDuration = 0.0;
9242  }
9243  else if(PointsChanged)
9244  {
9245  RouteFlashDuration = AllRoutes->PointsDelay * TempSpeedVal;
9246  }
9247  else
9248  {
9249  RouteFlashDuration = AllRoutes->SignalsDelay * TempSpeedVal;
9250  }
9251  ConstructRoute->SetRouteFlashValues(1, AutoSigsFlag, true); // true for PrefDirRoute
9253  }
9254  else
9255  {
9257  }
9258  Screen->Cursor = TCursor(-2); // Arrow
9259  TrainController->BaseTime = TDateTime::CurrentDateTime();
9261  Utilities->CallLogPop(65);
9262  return;
9263  }
9264  else
9265  {
9266  bool CallonFalse = false;
9267  if(ConstructRoute->GetNextNonPreferredRouteElement(0, HLoc, VLoc, CallonFalse, ConstructRoute->ReqPosRouteID, PointsChanged))
9268  {
9269  Track->RouteFlashFlag = true;
9270  PreferredRouteFlag = false;
9271  float TempSpeedVal = 1; // added for v2.3.0 to keep durations same as x1 values for slow speeds
9272  if(TTClockSpeed < 1)
9273  {
9274  TempSpeedVal = TTClockSpeed;
9275  }
9276  if(Level2OperMode == PreStart)
9277  {
9278  RouteFlashDuration = 0.0;
9279  }
9280  else if(PointsChanged)
9281  {
9282  RouteFlashDuration = AllRoutes->PointsDelay * TempSpeedVal;
9283  }
9284  else
9285  {
9286  RouteFlashDuration = AllRoutes->SignalsDelay * TempSpeedVal;
9287  }
9288  ConstructRoute->SetRouteFlashValues(2, false, false);
9290  }
9291  else
9292  {
9294  }
9295  }
9296  TrainController->BaseTime = TDateTime::CurrentDateTime();
9298  Screen->Cursor = TCursor(-2); // Arrow
9299  }
9300  Utilities->CallLogPop(66);
9301  return;
9302  }
9303  }
9304  Utilities->CallLogPop(68);
9305  }
9306  catch(const Exception &e)
9307  {
9308  ErrorLog(20, e.Message);
9309  }
9310 }
9311 
9312 // ---------------------------------------------------------------------------
9313 
9314 void TInterface::MainScreenMouseDown3(int Caller, TMouseButton Button, TShiftState Shift, int X, int Y)
9315 // ZoomOut mode
9316 {
9317 // NB: DisplayZoomOutOffsetH & V take account of the Min & Max H & V values so don't need these again
9318  try
9319  {
9320  AnsiString AnsiButton = "mbLeft";
9321  if(Button == mbRight)
9322  {
9323  AnsiButton = "mbRight";
9324  }
9325 
9326  TrainController->LogEvent("MainScreenMouseDown3," + AnsiButton + "," + AnsiString(X) + "," + AnsiString(Y));
9327  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",MainScreenMouseDown3," + AnsiButton + "," + AnsiString(X) +
9328  "," + AnsiString(Y));
9329  if(Button != mbLeft)
9330  {
9331  // this routine new at v2.1.0. Allows railway moving for zoom-out mode
9334  WholeRailwayMoving = true;
9335  Screen->Cursor = TCursor(-22); // Four arrows;
9336  AnsiString OldInfo = InfoPanel->Caption; //added at v2.22.0
9337  InfoPanel->Caption = "Railway moving, please don't click the mouse"; //added at v2.22.0
9338  InfoPanel->Visible = true;
9339  Display->Update(); //to show InfoPanel
9340  }
9341  else
9342  {
9343  InfoPanel->Visible = false; // reset infopanel in case not set later
9344  InfoPanel->Caption = "";
9345  int HRounding, VRounding;
9346  int TruePosH = (X / 4) + Display->DisplayZoomOutOffsetH;
9347  int TruePosV = (Y / 4) + Display->DisplayZoomOutOffsetV;
9348  // find nearest screen centre - from 30 to 210 horiz & from 18 to 126 vert
9349  if(TruePosH < 0)
9350  {
9351  HRounding = -(Utilities->ScreenElementWidth / 4);
9352  }
9353  else
9354  {
9355  HRounding = (Utilities->ScreenElementWidth / 4);
9356  }
9357  int CentreH = (((TruePosH + HRounding) / (Utilities->ScreenElementWidth / 2)) * (Utilities->ScreenElementWidth / 2));
9358  while((CentreH - Track->GetHLocMax()) >= (Utilities->ScreenElementWidth / 2))
9359  {
9360  CentreH -= (Utilities->ScreenElementWidth / 2);
9361  }
9362  while((Track->GetHLocMin() - CentreH) >= (Utilities->ScreenElementWidth / 2))
9363  {
9364  CentreH += (Utilities->ScreenElementWidth / 2);
9365  }
9366  if(TruePosV < 0)
9367  {
9368  VRounding = -(Utilities->ScreenElementHeight / 4);
9369  }
9370  else
9371  {
9372  VRounding = (Utilities->ScreenElementHeight / 4);
9373  }
9374  int CentreV = (((TruePosV + VRounding) / (Utilities->ScreenElementHeight / 2)) * (Utilities->ScreenElementHeight / 2));
9375  while((CentreV - Track->GetVLocMax()) >= (Utilities->ScreenElementHeight / 2))
9376  {
9377  CentreV -= (Utilities->ScreenElementHeight / 2);
9378  }
9379  while((Track->GetVLocMin() - CentreV) >= (Utilities->ScreenElementHeight / 2))
9380  {
9381  CentreV += (Utilities->ScreenElementHeight / 2);
9382  }
9383  Display->DisplayOffsetH = CentreH - (Utilities->ScreenElementWidth / 2);
9385 
9386  TLevel2OperMode TempLevel2OperMode = Level2OperMode;
9387  if(Level1Mode == BaseMode)
9388  {
9389  SetLevel1Mode(17);
9390  }
9391  else if(Level1Mode == TrackMode)
9392  {
9393  // set edit menu items
9395  PreventGapOffsetResetting = true; // when return from zoom by clicking screen don't force a return to the
9396  // displayed gap, user wants to display the clicked area
9397  SetLevel2TrackMode(32); // revert to earlier track mode from zoom
9398  PreventGapOffsetResetting = false;
9399  }
9400  else if(Level1Mode == PrefDirMode)
9401  {
9403  {
9404  SetLevel2PrefDirMode(3); // revert to earlier PrefDir mode from zoom
9405  }
9406  else
9407  {
9408  SetLevel1Mode(33); // if PrefDirSelecting revert to normap PrefDirMode
9409  }
9410  }
9411  // else if(Level1Mode == TrackMode) SetLevel1Mode();//just revert to basic track mode from zoom
9412  // else if(Level1Mode == PrefDirMode) SetLevel1Mode();//just revert to basic PrefDir mode from zoom
9413  else if(Level1Mode == TimetableMode)
9414  {
9415  InfoPanel->Visible = false;
9416  }
9417  // Not OperMode or RestartSessionOperMode as that resets the performance file
9418  else if(TempLevel2OperMode == Operating) // similar to SetLevel2OperMode but without resetting BaseTime
9419  {
9420  OperateButton->Enabled = true;
9421  OperateButton->Glyph->LoadFromResourceName(0, "PauseGraphic");
9422  ExitOperationButton->Enabled = true;
9424  }
9425  else if(TempLevel2OperMode == Paused) // similar to SetLevel2OperMode but without resetting RestartTime
9426  {
9427  OperateButton->Enabled = true;
9428  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
9429  ExitOperationButton->Enabled = true;
9430  TTClockAdjButton->Enabled = true;
9433  }
9434  else if(TempLevel2OperMode == PreStart)
9435  {
9436  OperateButton->Enabled = true;
9437  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
9438  ExitOperationButton->Enabled = true;
9439  TTClockAdjButton->Enabled = true;
9441  }
9442  Display->ZoomOutFlag = false; // reset this after level modes called so gap flash stays set if set to begin with
9445  }
9446  Utilities->CallLogPop(69);
9447  }
9448  catch(const Exception &e)
9449  {
9450  ErrorLog(21, e.Message);
9451  }
9452 }
9453 
9454 // ---------------------------------------------------------------------------
9455 
9456 void __fastcall TInterface::MainScreenMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
9457 {
9458  try
9459  {
9460  // TrainController->LogEvent("MainScreenMouseMove," + AnsiString(X) + "," + AnsiString(Y)); //dropped at v0.6, too many events
9461  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MainScreenMouseMove," + AnsiString(X) + "," + AnsiString(Y));
9462 
9463  if(!mbLeftDown && WholeRailwayMoving) // new at v2.1.0
9464  {
9465  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
9467  if(X < 0)
9468  {
9469  X = 0; // ensure pointer stays within display area
9470  }
9471  if(X > (MainScreen->Width - 1))
9472  {
9473  X = MainScreen->Width - 1;
9474  }
9475  if(Y < 0)
9476  {
9477  Y = 0;
9478  }
9479  if(Y > (MainScreen->Height - 1))
9480  {
9481  Y = MainScreen->Height - 1;
9482  }
9483  if(!Display->ZoomOutFlag)
9484  {
9485  int StartOffsetX = (X - StartWholeRailwayMoveHPos) % 16;
9486  int StartOffsetY = (Y - StartWholeRailwayMoveVPos) % 16;
9487 // if((abs(X - StartWholeRailwayMoveHPos) >= 16) || (abs(Y - StartWholeRailwayMoveVPos) >= 16)) //don't need this as railway won't move with less than 16 pixels
9488  { //since use int division by 16, also it causes a short move
9489  //followed by a longer move so better without it,
9490  //changed at v2.22.0
9491  int NewH = X - StartWholeRailwayMoveHPos;
9492  int NewV = Y - StartWholeRailwayMoveVPos;
9493  Display->DisplayOffsetH -= NewH / 16;
9494  Display->DisplayOffsetV -= NewV / 16;
9495  StartWholeRailwayMoveHPos = X - StartOffsetX;
9496  StartWholeRailwayMoveVPos = Y - StartOffsetY;
9499  {
9501  }
9502  }
9503  }
9504 
9505  else
9506  {
9507  int StartZOffsetX = (X - StartWholeRailwayMoveHPos) % 4;
9508  int StartZOffsetY = (Y - StartWholeRailwayMoveVPos) % 4;
9509 // if((abs(X - StartWholeRailwayMoveHPos) >= 4) || (abs(Y - StartWholeRailwayMoveVPos) >= 4)) //see above
9510  {
9511  int NewH = X - StartWholeRailwayMoveHPos;
9512  int NewV = Y - StartWholeRailwayMoveVPos;
9513  Display->DisplayZoomOutOffsetH -= NewH / 4;
9514  Display->DisplayZoomOutOffsetV -= NewV / 4;
9515  StartWholeRailwayMoveHPos = X - StartZOffsetX;
9516  StartWholeRailwayMoveVPos = Y - StartZOffsetY;
9517  Display->ClearDisplay(10);
9519  }
9520  }
9521  TrainController->BaseTime = TDateTime::CurrentDateTime();
9523  }
9524 
9525  else if(mbLeftDown)
9526  {
9528  /* [Repeated from MouseDown] - When 'select' chosen from the Edit menu (only available in 'AddTrack') conditions are set ready to enclose a rectangular screen area
9529  using MouseMove. When MouseDown occurs the starting point is marked (wrt whole railway, not just the screen) and stored in
9530  SelectStartPair. If the mouse button is released and a new start position selected then the earlier one is discarded. Providing the
9531  button is held down subsequent actions occur during MouseMove (to display the changing rectangle) and MouseUp to define the final
9532  selected rectangle.
9533  [New] At this point the select starting position has been defined in SelectStartPair, and the current mouse position is defined (wrt whole
9534  railway) in HLoc & VLoc from the screen positions X & Y by GetTrackLocsFromScreenPos. Both are incremented so that the rectangle
9535  includes the current point (if no mouse movement at all occurs then a 1 x 1 rectangle is displayed). Limits are set to prevent the
9536  displayed rectangle extending off screen. Edges are set at 60 & 36 rather than 59 & 35 because the defined rectangle excludes the
9537  rightmost and bottom HLoc & VLoc values, if 59 & 35 were used the right & bottom screen edges wouldn't be reached. A TRect is then
9538  defined from SelectStartPair and the HLoc/VLoc values, Clearand... called to clear earlier rectangles, and a dashed edge drawn round
9539  the selection.
9540  */
9541  {
9542  if(!MMoveTrackSelFlag)
9543  {
9544  TrainController->LogEvent("MouseMove + TrackSelecting");
9545  MMoveTrackSelFlag = true;
9546  }
9547  int CurrentHLoc, CurrentVLoc, StartHLoc = SelectStartPair.first, StartVLoc = SelectStartPair.second;
9548  Track->GetTrackLocsFromScreenPos(2, CurrentHLoc, CurrentVLoc, X, Y);
9549  // to make the rectangle inclusive of the start and current points, need to increase the HLoc value of the
9550  // rightmost point and the VLoc value of the bottommost point
9551  if(CurrentHLoc >= StartHLoc)
9552  {
9553  CurrentHLoc++;
9554  }
9555  else
9556  {
9557  StartHLoc++;
9558  }
9559  if(CurrentVLoc >= StartVLoc)
9560  {
9561  CurrentVLoc++;
9562  }
9563  else
9564  {
9565  StartVLoc++;
9566  }
9567  if(CurrentHLoc - Display->DisplayOffsetH > Utilities->ScreenElementWidth)
9568  {
9570  }
9571  if(CurrentVLoc - Display->DisplayOffsetV > Utilities->ScreenElementHeight)
9572  {
9574  }
9575  if(CurrentHLoc - Display->DisplayOffsetH < 0)
9576  {
9577  CurrentHLoc = Display->DisplayOffsetH;
9578  }
9579  if(CurrentVLoc - Display->DisplayOffsetV < 0)
9580  {
9581  CurrentVLoc = Display->DisplayOffsetV;
9582  }
9583  TRect TempRect(StartHLoc, StartVLoc, CurrentHLoc, CurrentVLoc);
9584  ClearandRebuildRailway(14); // to clear earlier rectangles
9585  Display->PlotDashedRect(0, TempRect);
9586  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
9587  }
9588 
9590  {
9591  if(!MMovePrefDirSelFlag)
9592  {
9593  TrainController->LogEvent("MouseMove + PrefDirSelecting");
9594  MMovePrefDirSelFlag = true;
9595  }
9596 
9597  int CurrentHLoc, CurrentVLoc, StartHLoc = SelectStartPair.first, StartVLoc = SelectStartPair.second;
9598  Track->GetTrackLocsFromScreenPos(5, CurrentHLoc, CurrentVLoc, X, Y);
9599  // to make the rectangle inclusive of the start and current points, need to increase the HLoc value of the
9600  // rightmost point and the VLoc value of the bottommost point
9601  if(CurrentHLoc >= StartHLoc)
9602  {
9603  CurrentHLoc++;
9604  }
9605  else
9606  {
9607  StartHLoc++;
9608  }
9609  if(CurrentVLoc >= StartVLoc)
9610  {
9611  CurrentVLoc++;
9612  }
9613  else
9614  {
9615  StartVLoc++;
9616  }
9617  if(CurrentHLoc - Display->DisplayOffsetH > Utilities->ScreenElementWidth)
9618  {
9620  }
9621  if(CurrentVLoc - Display->DisplayOffsetV > Utilities->ScreenElementHeight)
9622  {
9624  }
9625  if(CurrentHLoc - Display->DisplayOffsetH < 0)
9626  {
9627  CurrentHLoc = Display->DisplayOffsetH;
9628  }
9629  if(CurrentVLoc - Display->DisplayOffsetV < 0)
9630  {
9631  CurrentVLoc = Display->DisplayOffsetV;
9632  }
9633  TRect TempRect(StartHLoc, StartVLoc, CurrentHLoc, CurrentVLoc);
9634  ClearandRebuildRailway(57); // to clear earlier rectangles
9635  Display->PlotDashedRect(2, TempRect);
9636  Display->Update(); // need to keep this since Update() not called for PlotSmallOutput as too slow
9637  }
9638 
9640  /* [Repeated from MouseDown] - The same actions apply on MouseDown whether Copy or Cut selected from the menu. First the horizontal and vertical mouse position is
9641  checked and unless it lies within the selected rectangle and not within 4 pixels of an edge the pickup fails and the function returns.
9642  Otherwise flag SelectPickedUp is set to true (to allow it to move during MouseMove and remain in place at MouseUp) and the mouse position
9643  is saved in SelectBitmapMouseLocX & Y for use later in MouseMove & MouseUp.
9644  [New] - The same actions apply on MouseMove whether Copy or Cut selected from the menu. The X & Y mouse positions are checked and set to
9645  stay within the display area. Then the current selection H & V positions are stored in NewSelectBitmapHLoc & VLoc.
9646  These change continually while the mouse and the selection are moving, they are only read on MouseUp to retain the position that it then
9647  occupies. Clearand... is called finally to clear earlier selection displays.
9648  */
9649  {
9651  {
9652  TrainController->LogEvent("MouseMove + Copy or CutMoving & SelectPickedUp");
9654  }
9655  if(X < 0)
9656  {
9657  X = 0; // ensure pointer stays within display area
9658  }
9659  if(X > (MainScreen->Width - 1))
9660  {
9661  X = MainScreen->Width - 1;
9662  }
9663  if(Y < 0)
9664  {
9665  Y = 0;
9666  }
9667  if(Y > (MainScreen->Height - 1))
9668  {
9669  Y = MainScreen->Height - 1;
9670  }
9673  ClearandRebuildRailway(15); // plots SelectBitmap at the position given by NewSelectBitmapHLoc & ...VLoc
9674  }
9675 
9677  {
9679  {
9680  TrainController->LogEvent("MouseMove + MoveTextOrGraphic & TextFoundFlag");
9682  }
9684  NewHPos = TextOrUserGraphicGridVal * (div(NewHPos, TextOrUserGraphicGridVal).quot);
9686  NewVPos = TextOrUserGraphicGridVal * (div(NewVPos, TextOrUserGraphicGridVal).quot);
9687 
9688  TextHandler->TextPtrAt(26, TextItem)->HPos = NewHPos;
9689  TextHandler->TextPtrAt(27, TextItem)->VPos = NewVPos;
9691  }
9692 
9694  {
9696  {
9697  TrainController->LogEvent("MouseMove + MoveTextOrGraphic & UserGraphicFoundFlag");
9699  }
9701  NewHPos = TextOrUserGraphicGridVal * (div(NewHPos, TextOrUserGraphicGridVal).quot);
9703  NewVPos = TextOrUserGraphicGridVal * (div(NewVPos, TextOrUserGraphicGridVal).quot);
9704 
9708  }
9709  }
9710  Utilities->CallLogPop(70);
9711  }
9712  catch(const Exception &e)
9713  {
9714  ErrorLog(22, e.Message);
9715  }
9716 }
9717 
9718 // ---------------------------------------------------------------------------
9719 void __fastcall TInterface::MainScreenMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
9720 {
9721 /* [Repeated from MouseDown] - When 'select' chosen from the Edit menu (only available in 'AddTrack') conditions are set ready to enclose a rectangular screen area
9722  using MouseMove. When MouseDown occurs the starting point is marked (wrt whole railway, not just the screen) and stored in
9723  SelectStartPair. If the mouse button is released and a new start position selected then the earlier one is discarded. Providing the
9724  button is held down subsequent actions occur during MouseMove (to display the changing rectangle) and MouseUp to define the final
9725  selected rectangle.
9726  [Repeated from MouseMove] - At this point the select starting position has been defined in SelectStartPair, and the current mouse position is defined (wrt whole
9727  railway) in HLoc & VLoc from the screen positions X & Y by GetTrackLocsFromScreenPos. Both are incremented so that the rectangle
9728  includes the current point (if no mouse movement at all occurs then a 1 x 1 rectangle is displayed). Limits are set to prevent the
9729  displayed rectangle extending off screen. Edges are set at 60 & 36 rather than 59 & 35 because the defined rectangle excludes the
9730  rightmost and bottom HLoc & VLoc values, if 59 & 35 were used the right & bottom screen edges wouldn't be reached. A TRect is then
9731  defined from SelectStartPair and the HLoc/VLoc values, Clearand... called to clear earlier rectangles, and a dashed edge drawn round
9732  the selection.
9733  [New] This function can take some time so an hourglass cursor is displayed. The rectangle is fully defined, so the final screen X & Y
9734  values are translated into HLoc & VLoc values (wrt whole railway) and SelectEndPair set using them. The rectangle can be defined in any
9735  direction, so the end points may be before or after the starting points for both horizontal and vertical directions. Therefore the
9736  rectangle that will be used subsequently - SelectRect - is defined from SelectStart and SelectEnd allowing for any direction. Screen
9737  limits are set as during MouseMove, and a dashed edge drawn as before. Then a check is made to see if the final rectangle has any area,
9738  and if not 'Select' mode is kept and the function ends so that a new rectangle can be drawn, otherwise new menu items Cut, Copy & Delete,
9739  are enabled. Now the SelectBitmap is made ready by filling with white prior to the track bitmaps being copied. If this isn't done the
9740  track bitmaps are loaded from the top left hand corner and the rest becomes black - not what is wanted! The SelectVector (defined in
9741  TrackUnit) is then loaded with the elements enclosed by the rectangle, top to bottom and left to right, active track elements first then
9742  inactive track elements. Empty squares are ignored as are default (erased) elements. Now the SelectVector is read and the corresponding
9743  element bitmaps transferred to SelectBitmap in the appropriate positions, then a dashed border added. Finally the cursor is changed back
9744  to an arrow.
9745 */
9746  try
9747  {
9748  AnsiString AnsiButton = "mbLeft";
9749  if(Button == mbRight)
9750  {
9751  AnsiButton = "mbRight";
9752  }
9753  TrainController->LogEvent("MainScreenMouseUp," + AnsiButton + "," + AnsiString(X) + "," + AnsiString(Y));
9754  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MainScreenMouseUp," + AnsiButton + "," + AnsiString(X) + "," + AnsiString(Y));
9755  WholeRailwayMoving = false; // added at v2.1.0
9756  Screen->Cursor = TCursor(-2); // Arrow; (to reset from four arrows when moving) added at v2.1.0
9757  if(Display->ZoomOutFlag)
9758  {
9760  }
9761  else
9762  {
9763  InfoPanel->Caption = "";
9764  InfoPanel->Visible = false;
9765  Display->Update();
9766  }
9767  MMoveTrackSelFlag = false;
9768  MMovePrefDirSelFlag = false;
9772 
9774  {
9775  TrainController->LogEvent("MouseUp + TrackSelecting + mbLeftDown");
9776  Screen->Cursor = TCursor(-11); // Hourglass;
9777  int EndHLoc, EndVLoc, StartHLoc = SelectStartPair.first, StartVLoc = SelectStartPair.second;
9778  Track->GetTrackLocsFromScreenPos(3, EndHLoc, EndVLoc, X, Y); // these values don't allow for offsets so add in later
9779 // to make the rectangle inclusive of the start and current points, need to increase the HLoc value of the
9780 // rightmost point and the VLoc value of the bottommost point
9781  if(EndHLoc >= StartHLoc)
9782  {
9783  EndHLoc++;
9784  }
9785  else
9786  {
9787  StartHLoc++;
9788  }
9789  if(EndVLoc >= StartVLoc)
9790  {
9791  EndVLoc++;
9792  }
9793  else
9794  {
9795  StartVLoc++;
9796  }
9797  if(StartHLoc >= EndHLoc)
9798  {
9799  SelectRect.left = EndHLoc;
9800  SelectRect.right = StartHLoc;
9801  }
9802  else
9803  {
9804  SelectRect.left = StartHLoc;
9805  SelectRect.right = EndHLoc;
9806  }
9807  if(StartVLoc >= EndVLoc)
9808  {
9809  SelectRect.top = EndVLoc;
9810  SelectRect.bottom = StartVLoc;
9811  }
9812  else
9813  {
9814  SelectRect.top = StartVLoc;
9815  SelectRect.bottom = EndVLoc;
9816  }
9818  {
9820  }
9822  {
9824  }
9825  if(SelectRect.left - Display->DisplayOffsetH < 0)
9826  {
9828  }
9829  if(SelectRect.top - Display->DisplayOffsetV < 0)
9830  {
9832  }
9833  Level2TrackMode = AddTrack; // Level1Mode must be TrackMode
9834  SetLevel2TrackMode(69); // add all track elements so area can be filled with an element, must come before PlotDashedRect as calls Clearand...
9835  Level2TrackMode = TrackSelecting; // reset from AddTrack
9839  if((SelectRect.top == SelectRect.bottom) || (SelectRect.left == SelectRect.right))
9840  {
9841  SelectionValid = false;
9843  mbLeftDown = false;
9844  Screen->Cursor = TCursor(-2); // Arrow;
9845  Utilities->CallLogPop(71);
9846  return; // no rectangle
9847  }
9848  else
9849  {
9850  ReselectMenuItem->Enabled = false;
9851  CutMenuItem->Enabled = true;
9852  CopyMenuItem->Enabled = true;
9853  FlipMenuItem->Enabled = true;
9854  MirrorMenuItem->Enabled = true;
9855  RotRightMenuItem->Enabled = true;
9856  RotLeftMenuItem->Enabled = true;
9857  RotateMenuItem->Enabled = true;
9858 
9859 // PasteWithAttributesMenuItem->Enabled = false; //new menu item for v2.2.0 only enabled after cutting (dropped at 2.4.0 as all pastes are with attributes)
9860  DeleteMenuItem->Enabled = true;
9861  if(Track->IsTrackFinished())
9862  {
9863  SelectLengthsMenuItem->Enabled = true; // only permit if finished because reverts to DistanceStart
9864  }
9865  else
9866  {
9867  SelectLengthsMenuItem->Enabled = false; // and that can only be used if track linked
9868  }
9869  SelectBiDirPrefDirsMenuItem->Visible = false;
9870  CheckPrefDirConflictsMenuItem->Visible = false;
9871  CancelSelectionMenuItem->Enabled = true;
9872  // set SelectBitmap
9873  SelectBitmap->Width = (SelectRect.right - SelectRect.left) * 16;
9874  SelectBitmap->Height = (SelectRect.bottom - SelectRect.top) * 16;
9875 
9876  // fill it with transparent white (i.e. use Draw) else graphics all plot from top left hand corner
9877  for(int H = 0; H < (SelectBitmap->Width) / 16; H++)
9878  {
9879  for(int V = 0; V < (SelectBitmap->Height) / 16; V++)
9880  {
9881  SelectBitmap->Canvas->Draw(H * 16, V * 16, RailGraphics->bmSolidBgnd);
9882  // NB in above if use bmTransparent it ISN'T transparent, but if use the non-transparent bmSolidBgnd it IS transparent
9883  // presumably superimposing a transparent bitmap onto a transparent bitmap makes the result non-transparent!
9884  }
9885  }
9886 
9888  TTrackElement TempElement; // default element
9889  bool FoundFlag;
9890  //store active elements
9891  for(int x = SelectRect.left; x < SelectRect.right; x++)
9892  {
9893  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
9894  {
9895  int ATVecPos = Track->GetVectorPositionFromTrackMap(2, x, y, FoundFlag);
9896  if(FoundFlag)
9897  {
9898  TempElement = Track->TrackElementAt(440, ATVecPos);
9899  if(TempElement.SpeedTag > 0)
9900  {
9901  Track->SelectPush(TempElement); // don't store erase elements
9902  }
9903  }
9904  }
9905  }
9906  // now store inactive elements
9907  for(int x = SelectRect.left; x < SelectRect.right; x++)
9908  {
9909  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
9910  {
9911  TTrack::TIMPair IATVecPair = Track->GetVectorPositionsFromInactiveTrackMap(2, x, y, FoundFlag);
9912  if(FoundFlag)
9913  {
9914  TempElement = Track->InactiveTrackElementAt(30, IATVecPair.first);
9915  Track->SelectPush(TempElement); // only want SpeedTag & location set, rest defaults
9916  if(IATVecPair.second != IATVecPair.first) // 2 elements stored at location, i.e. platforms
9917  {
9918  TempElement = Track->InactiveTrackElementAt(31, IATVecPair.second);
9919  Track->SelectPush(TempElement);
9920  }
9921  }
9922  }
9923  }
9924 
9925  //store preferred directions //added at v2.9.0
9926  int PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3;
9927  TPrefDirElement TempPrefDirElement;
9929  for(int x = SelectRect.left; x < SelectRect.right; x++)
9930  {
9931  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
9932  {
9933  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(11, x, y, FoundFlag, PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3);
9934  if(FoundFlag)
9935  {
9936  if(PrefDirPos0 > -1)
9937  {
9938  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(2, PrefDirPos0);
9939  SelectPrefDir->ExternalStorePrefDirElement(6, TempPrefDirElement);
9940  }
9941  if(PrefDirPos1 > -1)
9942  {
9943  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(3, PrefDirPos1);
9944  SelectPrefDir->ExternalStorePrefDirElement(7, TempPrefDirElement);
9945  }
9946  if(PrefDirPos2 > -1)
9947  {
9948  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(4, PrefDirPos2);
9949  SelectPrefDir->ExternalStorePrefDirElement(8, TempPrefDirElement);
9950  }
9951  if(PrefDirPos3 > -1)
9952  {
9953  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(5, PrefDirPos3);
9954  SelectPrefDir->ExternalStorePrefDirElement(9, TempPrefDirElement);
9955  }
9956  }
9957  }
9958  }
9959 
9960  // store text items
9961  int LowSelectHPos = SelectRect.left * 16;
9962  int HighSelectHPos = SelectRect.right * 16;
9963  int LowSelectVPos = SelectRect.top * 16;
9964  int HighSelectVPos = SelectRect.bottom * 16;
9965  TextHandler->SelectTextVector.clear();
9966  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
9967  {
9968  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr < TextHandler->TextVector.end(); TextPtr++)
9969  {
9970  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos <
9971  HighSelectVPos))
9972  {
9973  // have to create a new TextItem in order to create a new Font object
9974  // BUT: only create new items where they don't appear as named location names
9975  // in SelectVector, since those names shouldn't be copied or pasted.
9976  // NB: altered for PasteWithAttributes - at v2.2.0 save the named element but prefix it with "##**"
9977  // so can paste or not depending on which type of paste is being used (unlikely to use that in a real name)
9978  bool SelectVectorNamedElement = false;
9979  AnsiString SelectTextString; // new at v2.2.0
9980  for(unsigned int x = 0; x < Track->SelectVector.size(); x++)
9981  {
9982  if(Track->SelectVector.at(x).LocationName == TextPtr->TextString)
9983  {
9984  SelectVectorNamedElement = true;
9985  break;
9986  }
9987  }
9988  if(SelectVectorNamedElement) // changed at v2.2.0
9989  {
9990  SelectTextString = "##**" + TextPtr->TextString; // new at v2.2.0
9991  }
9992  else // new at v2.2.0
9993  {
9994  SelectTextString = TextPtr->TextString;
9995  }
9996  TTextItem TextItem(TextPtr->HPos, TextPtr->VPos, SelectTextString, TextPtr->Font);
9997  TextHandler->SelectTextVector.push_back(TextItem); // changed at v2.2.0
9998  }
9999  }
10000  }
10001  // store graphic items, but first clear SelectGraphicVector
10002  Track->SelectGraphicVector.clear();
10003  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
10004  {
10005  for(TTrack::TUserGraphicVector::iterator UserGraphicPtr = Track->UserGraphicVector.begin(); UserGraphicPtr < Track->UserGraphicVector.end();
10006  UserGraphicPtr++)
10007  {
10008  if((UserGraphicPtr->HPos >= LowSelectHPos) && ((UserGraphicPtr->HPos + UserGraphicPtr->Width) < HighSelectHPos) &&
10009  (UserGraphicPtr->VPos >= LowSelectVPos) && ((UserGraphicPtr->VPos + UserGraphicPtr->Height) < HighSelectVPos))
10010  {
10011  Track->SelectGraphicVector.push_back(*UserGraphicPtr);
10012  }
10013  }
10014  }
10015 // new method - direct copying of existing selection so text included
10016  TRect Dest(0, 0, SelectBitmap->Width, SelectBitmap->Height);
10017  TRect Source(((SelectRect.left - Display->DisplayOffsetH) * 16), ((SelectRect.top - Display->DisplayOffsetV) * 16),
10018  ((SelectRect.right - Display->DisplayOffsetH) * 16), ((SelectRect.bottom - Display->DisplayOffsetV) * 16));
10019  SelectBitmap->Canvas->CopyRect(Dest, MainScreen->Canvas, Source);
10020  SelectionValid = true;
10021  }
10022  Screen->Cursor = TCursor(-2); // Arrow;
10023  }
10024 
10026  {
10027  TrainController->LogEvent("MouseUp + PrefDirSelecting + mbLeftDown");
10028  Screen->Cursor = TCursor(-11); // Hourglass;
10029 
10030  int EndHLoc, EndVLoc, StartHLoc = SelectStartPair.first, StartVLoc = SelectStartPair.second;
10031  Track->GetTrackLocsFromScreenPos(6, EndHLoc, EndVLoc, X, Y); // these values don't allow for offsets so add in later
10032 // to make the rectangle inclusive of the start and current points, need to increase the HLoc value of the
10033 // rightmost point and the VLoc value of the bottommost point
10034  if(EndHLoc >= StartHLoc)
10035  {
10036  EndHLoc++;
10037  }
10038  else
10039  {
10040  StartHLoc++;
10041  }
10042  if(EndVLoc >= StartVLoc)
10043  {
10044  EndVLoc++;
10045  }
10046  else
10047  {
10048  StartVLoc++;
10049  }
10050  if(StartHLoc >= EndHLoc)
10051  {
10052  SelectRect.left = EndHLoc;
10053  SelectRect.right = StartHLoc;
10054  }
10055  else
10056  {
10057  SelectRect.left = StartHLoc;
10058  SelectRect.right = EndHLoc;
10059  }
10060  if(StartVLoc >= EndVLoc)
10061  {
10062  SelectRect.top = EndVLoc;
10063  SelectRect.bottom = StartVLoc;
10064  }
10065  else
10066  {
10067  SelectRect.top = StartVLoc;
10068  SelectRect.bottom = EndVLoc;
10069  }
10071  {
10073  }
10075  {
10077  }
10078  if(SelectRect.left - Display->DisplayOffsetH < 0)
10079  {
10081  }
10082  if(SelectRect.top - Display->DisplayOffsetV < 0)
10083  {
10085  }
10089  if((SelectRect.top == SelectRect.bottom) || (SelectRect.left == SelectRect.right))
10090  {
10092  mbLeftDown = false;
10093  Screen->Cursor = TCursor(-2); // Arrow;
10094  Utilities->CallLogPop(1551);
10095  return; // no rectangle
10096  }
10097  else
10098  {
10099  SelectBiDirPrefDirsMenuItem->Enabled = true;
10100  CheckPrefDirConflictsMenuItem->Enabled = false;
10101  CancelSelectionMenuItem->Enabled = true;
10102  // don't need SelectBitmap for PrefDir selection
10103 
10104  // store active elements in Track->SelectVector, ignore inactive elements
10105  // clear the vector first
10107  TTrackElement TempElement; // default element
10108  bool FoundFlag;
10109  for(int x = SelectRect.left; x < SelectRect.right; x++)
10110  {
10111  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
10112  {
10113  int ATVecPos = Track->GetVectorPositionFromTrackMap(43, x, y, FoundFlag);
10114  if(FoundFlag)
10115  {
10116  TempElement = Track->TrackElementAt(729, ATVecPos);
10117  if(TempElement.SpeedTag > 0)
10118  {
10119  Track->SelectPush(TempElement); // don't store erase elements
10120  }
10121  }
10122  }
10123  }
10124  }
10125  Screen->Cursor = TCursor(-2); // Arrow;
10126  }
10127 
10129  /* [Repeated from MouseDown] - The same actions apply on MouseDown whether Copy or Cut selected from the menu. First the horizontal and vertical mouse position is
10130  checked and unless it lies within the selected rectangle and not within 4 pixels of an edge the pickup fails and the function returns.
10131  Otherwise flag SelectPickedUp is set to true (to allow it to move during MouseMove and remain in place at MouseUp) and the mouse position
10132  is saved in SelectBitmapMouseLocX & Y for use later in MouseMove & MouseUp.
10133  [Repeated from MouseMove] - The same actions apply on MouseMove whether Copy or Cut selected from the menu. The X & Y mouse positions are checked and set to
10134  stay within the display area. Then the current selection H & V positions are stored in NewSelectBitmapHLoc & VLoc.
10135  These change continually while the mouse and the selection are moving, they are only read on MouseUp to retain the position that it then
10136  occupies. Clearand... is called finally to clear earlier selection displays.
10137  [New] - The only action here is to transfer the values of NewSelectBitmapHLoc & VLoc to SelectBitmapHLoc & VLoc so that the selection
10138  stays in the same position (Clearand... checks whether the mouse is moving (both mbLeftDown & SelectPickedUp true) or stopped (either
10139  mbLeftDown or SelectPickedUp false) and uses NewSelectBitmapHLoc & VLoc or SelectBitmapHLoc & SelectBitmapVLoc respectively.
10140  */
10141  {
10142  TrainController->LogEvent("MouseUp + Copy or CutMoving + mbLeftDown + SelectPickedUp");
10145  }
10146  mbLeftDown = false;
10147  Track->CalcHLocMinEtc(11);
10148  Utilities->CallLogPop(72);
10149  }
10150  catch(const Exception &e)
10151  {
10152  ErrorLog(23, e.Message);
10153  }
10154 }
10155 
10156 // ---------------------------------------------------------------------------
10157 
10158 void __fastcall TInterface::MasterClockTimer(TObject *Sender)
10159 {
10160  try
10161  {
10162  // don't call LogEvent here as would occur too often
10163  // have to allow in zoomout mode
10164  if(ErrorLogCalledFlag)
10165  {
10166  return; // don't continue after an error
10167  }
10168  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MasterClockTimer");
10169  // put counter outside Clock2 as that may be missed
10170  LCResetCounter++;
10171 // this checks LCs every 20 clock ticks (1 sec) & raises barriers if no route & no train present, to avoid delays due to too frequent calls
10172  if(LCResetCounter > 19)
10173  {
10174  LCResetCounter = 0;
10175  }
10177  if(WarningFlashCount > 4)
10178  {
10179  WarningFlashCount = 0;
10180  }
10181  if(WarningFlashCount == 0)
10182  {
10184  }
10185  if(Utilities->CallLog.size() > 50) // use CTRL ALT 2 to see CallLogSize as program operates
10186  {
10187  throw Exception("Warning - Utilities->CallLog contains more than 50 items"); // check before clock stopped
10188  }
10190  // stopped during 'Paused', when modal windows appear - Popup menu & ShowMessage, and at other times
10191  {
10192  // RestartTime is TTClockTime when operation pauses (timetable start time initially),
10193  // BaseTime is CurrentDateTime() when operation restarts
10194 
10195 // clock speed multiplier
10196  double RealTimeDouble = double(TDateTime::CurrentDateTime() - TrainController->BaseTime);
10197  TrainController->TTClockTime = TDateTime(TTClockSpeed * RealTimeDouble) + TrainController->RestartTime;
10198 // TrainController->TTClockTime = TDateTime::CurrentDateTime() - TrainController->BaseTime + TrainController->RestartTime;
10199  }
10200 /*
10201 //elapsed time investigations - lose ~20% of ticks when nothing loaded & ~30% for a busy session
10202 //even lose ~20% without ClockTimer2. but this is when running in IDE, try outside - exactly the same.
10203 //see https://www.tek-tips.com/viewthread.cfm?qid=672717 - TTimer is very inaccurate, and the minimum interval is about 56ms because
10204 //of the PC clock frequency which is about 18Hz, so it always runs slow at short intervals. In light of this the 30% loss for a busy
10205 //session isn't too bad, it represents about 12% loss in reality.
10206 
10207 //Set breakpoint at int x = 4, load railway, start timer with Alt Ctrl 4 then check elapsed time when stops
10208 //no need to reload between checks,
10209 
10210  if(ElapsedTimeTestFunctionStart) //set in test function CTRL ALT 4
10211  {
10212  Start = double(GetTime()) * 86400; //secs
10213  ElapsedTimerRunning = true;
10214  TotalTicks = 0;
10215  ElapsedTimeTestFunctionStart = false;
10216  }
10217 
10218  if(ElapsedTimerRunning && TotalTicks < 100) //should take 5secs at 50ms, it's about 6.25secs with nothing loaded
10219  {
10220  TotalTicks++;
10221  }
10222  else if(ElapsedTimerRunning && TotalTicks >= 100)
10223  {
10224  End = double(GetTime()) * 86400;
10225  ElapsedTimerRunning = false;
10226 
10227  ElapsedTime = End - Start; //secs
10228 
10229  int x = 4; //breakpoint
10230  } //TrainController->TrainVector.size() <- Ctrl copy & paste into debugger window to examine
10231 //end of elapsed time code
10232 */
10234  {
10235  Utilities->CallLogPop(774);
10236  return;
10237  }
10238  Utilities->Clock2Stopped = true; // don't allow overlapping calls
10239  ClockTimer2(0);
10240  Utilities->Clock2Stopped = false;
10241  Utilities->CallLogPop(73);
10242 
10243  }
10244  catch(const Exception &e)
10245  {
10246  ErrorLog(24, e.Message);
10247  }
10248 }
10249 
10250 // ---------------------------------------------------------------------------
10251 
10252 void TInterface::ClockTimer2(int Caller)
10253 {
10254 // called every 50mSec
10255  try
10256  {
10257  // have to allow in zoomout mode
10258  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ClockTimer2");
10259 
10260  // dropped at 2.0.0 because RestoreFocusPanel->SetFocus(); hides the help screen
10261  // If a button holds focus then all that is needed is to click the screen and the arrow keys work correctly
10262 
10263 /* Dropped when new .chm help file introduced at v2.0.0 - this hid it after ~20ms. Replaced by a new section in
10264  MainScreenMouseDown where focus restored to screen when click anywhere on screen, allowing navigation keys to
10265  move screen when clicked if focus had been captured by another panel when these keys just cycle through the panel buttons
10266 
10267  bool FocusRestoreAllowedFlag = true; //added at v1.3.0
10268 
10269  if(TextBox->Focused() || DistanceBox->Focused() || SpeedLimitBox->Focused() || LocationNameTextBox->Focused() || MileEdit->Focused() || ChainEdit->Focused() || YardEdit->Focused() ||
10270  SpeedEditBox2->Focused() || LocationNameComboBox->Focused() || AddSubMinsBox->Focused() || SpeedEditBox->Focused() || PowerEditBox->Focused() || OneEntryTimetableMemo->Focused() ||
10271  AddPrefDirButton->Focused()) //Added at v1.3.0. If any of these has focus then they keep it until they release it. AddPrefDirButton is included as it should keep focus
10272  FocusRestoreAllowedFlag = false; //when it has it - eases the setting of PrefDirs, also this button becomes disabled after use so focus returns to Interface naturally
10273 
10274  if(!Focused() && FocusRestoreAllowedFlag && (GetAsyncKeyState(VK_LBUTTON) >= 0) && (GetAsyncKeyState(VK_RBUTTON) >= 0)) //condition added at v1.3.0 to ensure focus returned to
10275  //Interface (so arrow keys work to move screen) & not left at any of the buttons or other Windows controls
10276  //include the Windows API functions to test that the mouse buttons are not down (strictly only need left but user may have mapped the left onto the right so test both) - if not
10277  //tested then don't always respond to button clicks on navigation and other buttons because the focus can be grabbed back from the button by RestoreFocusPanel before the button
10278  //can respond (takes about 200mSec from click to response) a delay is also included to doubly avoid the button losing focus as above
10279  {
10280  ClockTimer2Count++; //doesn't matter what value it starts at on first use, it will soon revert to 0
10281  if(ClockTimer2Count > 10) ClockTimer2Count = 0; //half second delay
10282  if(ClockTimer2Count == 0)
10283  {
10284  RestoreFocusPanel->Visible = true;
10285  RestoreFocusPanel->Enabled = true;
10286  RestoreFocusPanel->BringToFront();
10287  //RestoreFocusPanel->SetFocus(); //to remove focus from anything else
10288  RestoreFocusPanel->Enabled = false; //to remove focus from RestoreFocusPanel & return it to Interface
10289  RestoreFocusPanel->Visible = false;
10290  }
10291  }
10292  else ClockTimer2Count = 0; //reset to 0 so ensure full delay occurs before RestoreFocusPanel grabs focus from anything else
10293 */
10294  CallLogTickerLabel->Caption = Utilities->CallLog.size(); // diagnostic test function to ensure all CallLogs are popped - visibility
10295  // toggled by 'Ctrl Alt 2' when Interface form has focus
10296 
10297  // set current time
10298  TDateTime Now = TrainController->TTClockTime;
10299 
10300  if((ActionsDueForm->Visible) && (LCResetCounter == 0)) //set every second, LCResetCounter used for convenience as that resets every second
10301  { //delay is
10302  ADFTop = ActionsDueForm->Top; //stores the ADForm position for re-use when becomes visible. This form intermittently jumped to other monitor
10303  ADFLeft = ActionsDueForm->Left; //but hasn't done it recently. This is a safeguard to hopefully prevent it in future.
10304  }
10305 
10306  //repair any failed infrastructure
10307  //points
10308  if(!Track->FailedPointsVector.empty() && (Level2OperMode != Paused) && !TrainController->StopTTClockFlag) //don't repair if clock stopped or adj panel open
10309  {
10310  for(TTrack::TFailedElementVector::iterator FPVIt = Track->FailedPointsVector.begin(); FPVIt < Track->FailedPointsVector.end(); FPVIt++)
10311  {
10312  if(Now > FPVIt->RepairTime)
10313  {
10314  Track->RepairFailedPoints(FPVIt);
10315  break; //stop after repaired one as erased from vector, if others then will repair in later clock cycles
10316  }
10317  }
10318  }
10319  //signals
10320  if(!Track->FailedSignalsVector.empty() && (Level2OperMode != Paused) && !TrainController->StopTTClockFlag) //don't repair if clock stopped or adj panel open
10321  {
10322  for(TTrack::TFailedElementVector::iterator FPVIt = Track->FailedSignalsVector.begin(); FPVIt < Track->FailedSignalsVector.end(); FPVIt++)
10323  {
10324  if(Now > FPVIt->RepairTime)
10325  {
10326  Track->RepairFailedSignals(FPVIt);
10327  break; //stop after repaired one as erased from vector, if others then will repair in later clock cycles
10328  }
10329  }
10330  }
10331  //TSRs
10332  if(!Track->TSRVector.empty() && (Level2OperMode != Paused) && !TrainController->StopTTClockFlag) //don't repair if clock stopped or adj panel open
10333  {
10334  for(TTrack::TFailedElementVector::iterator FPVIt = Track->TSRVector.begin(); FPVIt < Track->TSRVector.end(); FPVIt++)
10335  {
10336  if(Now > FPVIt->RepairTime)
10337  {
10338  Track->RepairTSR(FPVIt);
10339  break; //stop after repaired one as erased from vector, if others then will repair in later clock cycles
10340  }
10341  }
10342  }
10343 
10344 //check if Temporary Speed Restriction (TSR) needed - check every minute of timetable time - added at v2.13.0 //changed from 5 mins to 1 min at v2.14.0
10345  if((TrainController->TTClockTime - Utilities->LastTSRCheckTime) > TDateTime(1.0 / 1440))
10346  {
10347  //check due now
10349  { //(Level2OperMode != PreStart) added at v2.13.2 to prevent a TSR before starts
10351  int TSRRandVal = (Utilities->MTBTSRs * 1440) / Track->SimpleVector.size(); //1440 = no. of mins/day
10352  if(TSRRandVal < 2)
10353  {
10354  TSRRandVal = 2; //in case so many simple elements that one fails every minute, this gives at least 2 mins between failures
10355  }
10356  if(random(TSRRandVal) == 0) //chance of one failure in the railway with MTBTSRs in days/simple element
10357  {
10358  //now identify the specific simple element
10359  int SimpleTVPos = Track->SimpleVector.at(random(Track->SimpleVector.size())); //vector of simple elements in the railway
10360  TTrackElement &TE = Track->TrackElementAt(1531, SimpleTVPos);
10361  if(!TE.Failed) //skip if already failed, added at v2.13.2 in response to Thomas Groenewold error file sent in 24/09/22
10362  { //error file showed that the same element had failed twice, so after repaired the first time .Failed was set to false
10363  //but the element remained in the TSRVector and crashed when tried to repair a second time
10365  IFE.TVPos = SimpleTVPos;
10366  TE.Failed = true;
10367  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = TE.SpeedLimit01; //store these values temporarily, simple elements aren't bridges so can use these
10368  TE.SpeedLimit01 = 10; //value while failed
10369  Display->WarningLog(23, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Temporary Speed Restriction at " + TE.ElementID);
10370  PerfLogForm->PerformanceLog(46, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Temporary Speed Restriction at " + TE.ElementID);
10371  TrainController->StopTTClockMessage(133, "Temporary Speed Restriction imposed at " + TE.ElementID +
10372  "\nSpeed limit of 10km/hour applies until track repaired.");
10373  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
10374  //set repair time, random value in minutes between 10 and 179
10375  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
10376  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
10377  IFE.RepairTime = RepairTime;
10379  Track->TSRVector.push_back(IFE);
10380  }
10381  }
10382  }
10383  }
10384  if(!ActionsDueForm->ActionsDueListBox->MouseInClient) // added at v2.7.0 to reset this flag whenever mouse not in ActionsDueListBox
10385  {
10387  }
10388  if(!ClipboardChecked)
10389  {
10390  if((Level1Mode == TrackMode) && !SelectionValid && (ClpBrdValid != "RlyClpBrd_Cut") && (ClpBrdValid != "RlyClpBrdCopy"))
10391  // reset the menu for the new app (when !SelectionValid) & don't keep resetting when ClpBrdValid
10392  //before 2.10.0 one '&' was missing before '(ClpBrdValid != "RlyClpBrd_Cut")' making it into an address which would always be > 0 i.e.true
10393  {
10394  SetTrackModeEditMenu(2); // to reset the menu in case select a new app for pasting
10395  ClipboardChecked = true;
10396  }
10397  }
10398 // Update Displayed Clock - resets to 0 at 96hours
10400 
10401 //timers
10405  {
10407  }
10408  if(ActionsDueForm->Visible)
10409  {
10411  }
10414  {
10415  TrainController->OpActionPanelHintDelayCounter = 60; // new at v2.2.0
10416  }
10417  TrainController->RandomFailureCounter++; // new at v2.4.0 counts up for 53 seconds then resets
10419  {
10421  }
10422 
10423 //multiplayer functions - disabled at v2.12.0 as not needed yet
10424 /*
10425  PlayerFiveSecondTimer++; // new for multiplayer for player send cycle
10426  if(PlayerFiveSecondTimer >= 100)
10427  {
10428  PlayerFiveSecondTimer = 0;
10429  }
10430 
10431  PlayerOneSecondTimer++; // new for multiplayer for player cancel
10432  if(PlayerOneSecondTimer >= 20)
10433  {
10434  PlayerOneSecondTimer = 0;
10435  }
10436 
10437  HostMultiplayInProgressFlag = false;
10438  PlayerMultiplayInProgressFlag = false;
10439  if(MultiplayerHostPanel->Visible || HostInSessionFlag)
10440  {
10441  HostMultiplayInProgressFlag = true;
10442  }
10443  else if(MultiplayerPlayerPanel->Visible || PlayerInSessionFlag)
10444  {
10445  PlayerMultiplayInProgressFlag = true;
10446  }
10447 
10448  if(Level2OperMode == PreStart)
10449  {
10450  if(!MultiplayerHostPanel->Visible && !MultiplayerPlayerPanel->Visible)
10451  {
10452  MultiplayerMenu->Enabled = true;
10453  MultiplayerHostSessionMenuItem->Enabled = true;
10454  SaveMultiplayerSessionMenuItem->Enabled = false;
10455  EndSimulationMenuItem->Enabled = false;
10456  ShowHideStringGridMenuItem->Enabled = false;
10457  JoinMultiplayerSessionMenuItem->Enabled = true;
10458  ExitSimulationMenuItem->Enabled = false;
10459  }
10460  else
10461  {
10462  MultiplayerMenu->Enabled = false;
10463  }
10464  }
10465 
10466  if(HostInSessionFlag && !PlayerInSessionFlag)
10467  {
10468 
10469  MultiplayerMenu->Enabled = true;
10470  MultiplayerHostSessionMenuItem->Enabled = false;
10471  SaveMultiplayerSessionMenuItem->Enabled = true;
10472  EndSimulationMenuItem->Enabled = true;
10473  ShowHideStringGridMenuItem->Enabled = true;
10474  JoinMultiplayerSessionMenuItem->Enabled = false;
10475  ExitSimulationMenuItem->Enabled = false;
10476  }
10477  else if(!HostInSessionFlag && PlayerInSessionFlag)
10478  {
10479 
10480  MultiplayerMenu->Enabled = true;
10481  MultiplayerHostSessionMenuItem->Enabled = false;
10482  SaveMultiplayerSessionMenuItem->Enabled = false;
10483  EndSimulationMenuItem->Enabled = false;
10484  ShowHideStringGridMenuItem->Enabled = false;
10485  JoinMultiplayerSessionMenuItem->Enabled = false;
10486  ExitSimulationMenuItem->Enabled = true;
10487  }
10488 
10489  if(HostMultiplayInProgressFlag)
10490  {
10491  HostHandshakingActions();
10492  }
10493  else if(PlayerMultiplayInProgressFlag)
10494  {
10495  PlayerHandshakingActions();
10496  }
10497 */
10498 //end of multiplayer functions
10499 
10500 // Below added at v2.1.0 to ensure WholeRailwayMoving flag reset when not moving (when rh mouse button up) as sometimes misses
10501 // MouseUp events, probably due to a clash between a moving event and a mouse up event. Note that checks that both mouse buttons are up because
10502 // function only checks the physical buttons, not the logical buttons. Most sig bit of return value set form key down.
10503  if(WholeRailwayMoving && (GetAsyncKeyState(VK_LBUTTON) >= 0) && (GetAsyncKeyState(VK_RBUTTON) >= 0))
10504  {
10505  WholeRailwayMoving = false;
10506  Screen->Cursor = TCursor(-2); // Arrow
10507  }
10508 // save session if required
10509  if(SaveSessionFlag)
10510  {
10511  SaveSession(0);
10512  SaveSessionFlag = false;
10513  }
10514 // load session if required
10515  if(LoadSessionFlag)
10516  {
10517  if(ClearEverything(3))
10518  {
10519  LoadSession(0);
10520  }
10521  LoadSessionFlag = false;
10522  }
10523 // check if any LCs need barriers raising
10524 
10526  {
10528  {
10529  for(int x = Track->BarriersDownVector.size() - 1; x >= 0; x--) // iterate downwards because erase element
10530  {
10531  bool TrainPresent = false;
10533  Track->BarriersDownVector.at(x).VLoc, ConstructRoute->SearchVector, TrainPresent)) // returns true for route (set or being set) or train, and TrainPresent true if train on LC
10534  {
10535  if(TrainPresent)
10536  {
10537  Track->BarriersDownVector.at(x).ReducedTimePenalty = true;
10538 // to allow 3 mins before time penalty starts to clock up, if no train passes then no time allowance
10539  }
10540  }
10541  else
10542  {
10543  if(Track->BarriersDownVector.at(x).TypeOfRoute != 2) // added at v2.6.0 for manual LC operation
10544  {
10545  Track->LCChangeFlag = true;
10547  // check if have exceeded the allowance (3 minutes for a train having passed or 0 for not) and add it to the overall excess time
10548  TDateTime TempExcessLCDownTime;
10549  if(Track->BarriersDownVector.at(x).ReducedTimePenalty)
10550  {
10551  TempExcessLCDownTime = TrainController->TTClockTime - CLC.StartTime - TDateTime(180.0 / 86400);
10552  }
10553  else
10554  {
10555  TempExcessLCDownTime = TrainController->TTClockTime - CLC.StartTime;
10556  }
10557  if(TempExcessLCDownTime > TDateTime(0))
10558  {
10559  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
10560  }
10561  CLC.StartTime = TrainController->TTClockTime; // reset these 3 members
10564  Track->SetLinkedLevelCrossingBarrierAttributes(0, CLC.HLoc, CLC.VLoc, 2); // set attr to 2 for changing state
10565  Track->ChangingLCVector.push_back(CLC);
10566  Track->BarriersDownVector.erase(Track->BarriersDownVector.begin() + x);
10567  }
10568  }
10569  }
10570  }
10571  }
10572 // clear LCChangeFlag if no LCs changing
10573  if(Track->ChangingLCVector.empty())
10574  {
10575  Track->LCChangeFlag = false;
10576  }
10577 // remove any single route elements if operating, but only if not constructing a route, else if extending the single route
10578 // element it may be removed prior to conversion & cause an error
10579 
10580 // note that if a train enters at a continuation and a signal is next but one to the continuation then the route element at that
10581 // signal won't be removed because the train's LagElement is still -1 and trains only remove route elements when LagElement is > -1.
10582 // This also means that a preferred route can't be cancelled as it's under a train, but it's probably not worth adding a patch just for
10583 // this, it shouldn't interfere with operation.
10585  {
10586  bool ElementRemovedFlag = false; // introduced at v0.6 to avoid calling Clearand.... multiple times
10587  for(unsigned int x = 0; x < AllRoutes->AllRoutesSize(); x++)
10588  {
10589  if(AllRoutes->GetFixedRouteAt(187, x).PrefDirSize() == 1)
10590  {
10591  // only allow route element to be removed if not selected for a route start otherwise StartSelectionRouteID will be
10592  // set & will fail at convert
10594  {
10596  // also don't remove if it links two automatic signal routes (reported by Daniel Gill for Darlington via discord on 13/12/20)
10597  // added at v2.6.1
10598  // note that a train will still remove the route element when it reaches it because of the 3rd condition below, but it will be removed when the train
10599  // is half on the preceding element rather than fully on it, in other cases the train has to be fully on the element because the route only becomes a
10600  // single element at that stage
10601  unsigned int LinkFromTVNumber = Track->TrackElementAt(1007, PDE.GetTrackVectorPosition()).Conn[PDE.GetELinkPos()];
10602  unsigned int LinkToTVNumber = Track->TrackElementAt(1008, PDE.GetTrackVectorPosition()).Conn[PDE.GetXLinkPos()];
10603  int RouteNumber1, RouteNumber2, TrainID; // not used
10604  if((AllRoutes->GetRouteTypeAndNumber(37, LinkFromTVNumber, PDE.GetELinkPos(), RouteNumber1) != TAllRoutes::AutoSigsRoute) ||
10605  (AllRoutes->GetRouteTypeAndNumber(38, LinkToTVNumber, PDE.GetXLinkPos(), RouteNumber2) != TAllRoutes::AutoSigsRoute) ||
10606  (Track->TrackElementAt(1009, LinkFromTVNumber).TrainIDOnElement > -1))
10607  // don't need to test for it being a bridge as then LinkFromTVNumber can't be
10608  // an autosigs route
10609  {
10610  AllRoutes->RemoveRouteElement(20, PDE.HLoc, PDE.VLoc, PDE.GetELink());
10611  ElementRemovedFlag = true;
10612  TrainController->LogEvent("SingleRouteElementRemoved, H = " + AnsiString(PDE.HLoc) + ", V = " + AnsiString(PDE.VLoc));
10613  }
10614  }
10615  }
10616  }
10617  if(!Display->ZoomOutFlag && ElementRemovedFlag)
10618  {
10620  }
10621  // if zoomed out ignore, will display correctly when zoom in
10622  // if leave the Zoomout condition out then the zoom out will spontaneously cancel and the track won't display because
10623  // PlotOutput returns if zoomed out, and the zoom out flag isn't reset until the end of Clearand.....
10624  // this was moved outside the for.. next.. loop in v0.6 as it could be called multiple times and slowed down operation (noticeable with a fast clock)
10625  }
10626 
10627 // stop clock if hover over a warning
10628  bool WH1 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog1->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog1->Width + OutputLog1->Left))
10629  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog1->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog1->Height + OutputLog1->Top))
10630  && OutputLog1->Caption != "";
10631  bool WH2 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog2->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog2->Width + OutputLog2->Left))
10632  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog2->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog2->Height + OutputLog2->Top))
10633  && OutputLog2->Caption != "";
10634  bool WH3 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog3->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog3->Width + OutputLog3->Left))
10635  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog3->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog3->Height + OutputLog3->Top))
10636  && OutputLog3->Caption != "";
10637  bool WH4 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog4->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog4->Width + OutputLog4->Left))
10638  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog4->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog4->Height + OutputLog4->Top))
10639  && OutputLog4->Caption != "";
10640  bool WH5 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog5->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog5->Width + OutputLog5->Left))
10641  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog5->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog5->Height + OutputLog5->Top))
10642  && OutputLog5->Caption != "";
10643  bool WH6 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog6->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog6->Width + OutputLog6->Left))
10644  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog6->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog6->Height + OutputLog6->Top))
10645  && OutputLog6->Caption != "";
10646  bool WH7 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog7->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog7->Width + OutputLog7->Left))
10647  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog7->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog7->Height + OutputLog7->Top))
10648  && OutputLog7->Caption != "";
10649  bool WH8 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog8->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog8->Width + OutputLog8->Left))
10650  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog8->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog8->Height + OutputLog8->Top))
10651  && OutputLog8->Caption != "";
10652  bool WH9 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog9->Left) && (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog9->Width + OutputLog9->Left))
10653  && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog9->Top) && (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog9->Height + OutputLog9->Top))
10654  && OutputLog9->Caption != "";
10655  bool WH10 = (Mouse->CursorPos.x >= ClientOrigin.x + OutputLog10->Left) &&
10656  (Mouse->CursorPos.x < (ClientOrigin.x + OutputLog10->Width + OutputLog10->Left)) && (Mouse->CursorPos.y >= ClientOrigin.y + OutputLog10->Top) &&
10657  (Mouse->CursorPos.y < (ClientOrigin.y + OutputLog10->Height + OutputLog10->Top)) && OutputLog10->Caption != "";
10658 
10659  if(WH1 || WH2 || WH3 || WH4 || WH5 || WH6 || WH7 || WH8 || WH9 || WH10 || SkipTTActionsListBox->Visible || ReminderListBox->Visible)
10660  {
10661  if(!WarningHover)
10662  {
10663  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
10665  WarningHover = true;
10666  }
10667  }
10668  else if(WarningHover)
10669  {
10670  WarningHover = false;
10671  TrainController->BaseTime = TDateTime::CurrentDateTime();
10673  }
10674 
10675 /* dropped at v2.15.1 as it hid the help screen and still made the navigation keys cycle round panels if a panel button had been pressed - e.g. click
10676 pause or run and it cycled round the operate panel buttons
10677 
10678  //below added at v2.13.2 so focus restored after actions due or perflog windows take focus by enabling, disabling or
10679  //moving. Otherwise the track info shortcut keys don't work (& probably others)
10680  bool MouseInInterface = ((Mouse->CursorPos.x >= ClientOrigin.x) && (Mouse->CursorPos.x < (ClientOrigin.x + Interface->Width))
10681  && (Mouse->CursorPos.y >= ClientOrigin.y) && (Mouse->CursorPos.y < (ClientOrigin.y + Interface->Height)));
10682 
10683  if(!Interface->Active && MouseInInterface)
10684  {
10685  Interface->SetFocus();
10686  }
10687 */
10688  if(ActionsDueForm->Active || PerfLogForm->Active) //in place of above at v2.15.1
10689  {
10690  Interface->SetFocus();
10691  }
10692 
10693 // development panel - visibility toggled by 'Ctrl Alt 3' when Interface form has focus
10694  if(DevelopmentPanel->Visible)
10695  {
10696  int Position;
10697  TTrackElement TrackElement;
10698  AnsiString Type[15] =
10699  {
10700  "Simple", "Crossover", "Points", "Buffers", "Bridge", "SignalPost", "Continuation", "Platform", "GapJump", "FootCrossing", "Unused", "Concourse",
10701  "Parapet", "NamedNonStationLocation", "Erase"
10702  };
10703 
10704  int OffH = Display->DisplayOffsetH; //added at v2.20.2 to help in converting Posx & y to equivalent offset values
10705  int OffV = Display->DisplayOffsetV;
10706  int ScreenX = Mouse->CursorPos.x - MainScreen->ClientOrigin.x;
10707  int ScreenY = Mouse->CursorPos.y - MainScreen->ClientOrigin.y;
10708  int HLoc, VLoc;
10709  AnsiString MouseStr = "Posx: " + AnsiString(ScreenX) + "; Posy: " + AnsiString(ScreenY);
10710  DevelopmentPanel->Caption = CurDir + " " + MouseStr + "; OffH " + OffH + ";OffV " + OffV;
10711  Track->GetTrackLocsFromScreenPos(7, HLoc, VLoc, ScreenX, ScreenY);
10712 
10713  AnsiString InARoute = "No"; //added at v2.15.0 for diagnostics
10714  THVPair HVPair;
10715  HVPair.first = HLoc;
10716  HVPair.second = VLoc;
10717  int RouteNumber = 9999;
10718  int RouteID = 9999;
10719  unsigned int RoutePrefDirPos = 0;
10721  AnsiString COS = "No";
10722 
10723  if(Track->FindNonPlatformMatch(1, HLoc, VLoc, Position, TrackElement))
10724  {
10725  if(AllRoutes->TrackIsInARoute(21, Position, 0)) //added at v2.15.0 for diagnostics
10726  {
10727  InARoute = "Yes";
10728  R2It = AllRoutes->Route2MultiMap.find(HVPair);
10729  RouteNumber = R2It->second.first;
10730  RoutePrefDirPos = R2It->second.second;
10731  RouteID = AllRoutes->GetFixedRouteAt(232, RouteNumber).RouteID;
10732  }
10733  if(TrackElement.CallingOnSet)
10734  {
10735  COS = "Yes";
10736  }
10737 
10738  DevelopmentPanel->Caption = MouseStr + "; OffH " + OffH + ";OffV " + OffV + "; TVPos: " + AnsiString(Position) + "; H: " + AnsiString(HLoc) + "; V: " +
10739  AnsiString(VLoc) + "; SpTg: " + AnsiString(TrackElement.SpeedTag) + "; Type: " + Type[TrackElement.TrackType] + "; Att: " + AnsiString(TrackElement.Attribute)
10740  + "; COS: " + COS + "; SPos1: " + AnsiString(TrackElement.StationEntryStopLinkPos1) + "; SPos2: " + AnsiString(TrackElement.StationEntryStopLinkPos2)
10741  + "; SPos3: " + AnsiString(TrackElement.StationEntryStopLinkPos3) + "; SPos4: " + AnsiString(TrackElement.StationEntryStopLinkPos4)
10742  + "; TrID: " + AnsiString(TrackElement.TrainIDOnElement) + "; TrID01: " + AnsiString(TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01) +
10743  "; TrID23: " + AnsiString(TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23) + "; Locname: " + TrackElement.LocationName + "; Activename: " +
10744  TrackElement.ActiveTrackElementName + "; InRoute " + InARoute + "; RtNum " + RouteNumber + "; RtID " + RouteID + "; PDVecPos " + RoutePrefDirPos + " Links: " +
10745  TrackElement.Link[0] + "," + TrackElement.Link[1] + "," + TrackElement.Link[2] + "," + TrackElement.Link[3] + " CLPos " +
10746  TrackElement.ConnLinkPos[0] + "," +TrackElement.ConnLinkPos[1] + "," +TrackElement.ConnLinkPos[2] + "," +TrackElement.ConnLinkPos[3];
10747  // + "; OAHintCtr: " + TrainController->OpActionPanelHintDelayCounter;
10748  }
10749  else
10750  {//below used in elapsed time investigations
10751 // DevelopmentPanel->Caption = FormatFloat("#####00.000", (ElapsedTime * 3600 * 24)) + " " + AnsiString(TotalTicks);
10752  }
10753  }
10754  if(Level1Mode == TimetableMode)
10755  {
10756 /* These are for Shift Key shortcuts. Unless 'Click()' execution occurs after the key is pressed Windows stores the key until after any code is executed then selects
10757 the timetable entry that begins with the letter corresponding to the key. See DevHistory.txt for the version at v2.5.0 for details.
10758 
10759 First make sure the selected entry is the Highlighted entry, but only if both mouse buttons are up, to make sure AllEntriesTTListBoxMouseUp runs first or TopIndex
10760 likely to be set to the wrong position since when ...Selected... runs it sets TopIndex accordingly. Then when ...MouseUp runs it will use the wrong value and select
10761 the entry that the mouse is now on rather than the one that was chosen.
10762 Later addition: Set member variable AllEntriesTTListBox->TopIndex here if any flag set so when Copy or any other key function runs the top index is correct
10763 */
10764  if((GetKeyState(VK_LBUTTON) >= 0) && (GetKeyState(VK_RBUTTON) >= 0) && (TTCurrentEntryIterator != TimetableEditVector.end())) //no mouse key down & pointer > 0
10765  // high order bit set to 1 when button down, so arithmetically it is negative
10766  {
10767  // TTCurrentEntryIterator == 0 when create a timetable
10768  AllEntriesTTListBox->Selected[TTCurrentEntryIterator - TimetableEditVector.begin()] = true; //selects the current entry &
10769  } //makes it visible in the listbox
10770  if(AnyTTKeyFlagSet()) // true if any of the below flags set
10771  {
10772  AllEntriesTTListBox->TopIndex = AllEntriesTTListBoxTopPosition; // reset it to the value before the key press changes it (see FormKeyDown)
10773  }
10775  {
10776  PreviousTTEntryButton->Click();
10777  SetTopIndex(0);
10778  PreviousTTEntryKeyFlag = false;
10779  }
10780  else if(NextTTEntryKeyFlag)
10781  {
10782  NextTTEntryButton->Click();
10783  SetTopIndex(1);
10784  NextTTEntryKeyFlag = false;
10785  }
10786  else if(MoveTTEntryUpKeyFlag)
10787  {
10788  MoveTTEntryUpButton->Click();
10789  SetTopIndex(2);
10790  MoveTTEntryUpKeyFlag = false;
10791  }
10792  else if(MoveTTEntryDownKeyFlag)
10793  {
10794  MoveTTEntryDownButton->Click();
10795  SetTopIndex(3);
10796  MoveTTEntryDownKeyFlag = false;
10797  }
10798  else if(CopyTTEntryKeyFlag)
10799  {
10800  CopyTTEntryButton->Click();
10801  SetTopIndex(4);
10802  CopyTTEntryKeyFlag = false;
10803  }
10804  else if(InvertTTEntryKeyFlag)
10805  {
10806  InvertTTEntryButton->Click();
10807  SetTopIndex(19);
10808  InvertTTEntryKeyFlag = false;
10809  }
10810  else if(CutTTEntryKeyFlag)
10811  {
10812  CutTTEntryButton->Click();
10813  SetTopIndex(5);
10814  CutTTEntryKeyFlag = false;
10815  }
10816  else if(PasteTTEntryKeyFlag)
10817  {
10818  PasteTTEntryButton->Click();
10819  SetTopIndex(6);
10820  PasteTTEntryKeyFlag = false;
10821  }
10822  else if(DeleteTTEntryKeyFlag)
10823  {
10824  DeleteTTEntryButton->Click();
10825  SetTopIndex(7);
10826  DeleteTTEntryKeyFlag = false;
10827  }
10828  else if(NewTTEntryKeyFlag)
10829  {
10830  NewTTEntryButton->Click();
10831  SetTopIndex(8);
10832  NewTTEntryKeyFlag = false;
10833  }
10834  else if(AZOrderKeyFlag)
10835  {
10836  AZOrderButton->Click();
10837  SetTopIndex(9);
10838  AZOrderKeyFlag = false;
10839  }
10840  else if(TimeOrderKeyFlag)
10841  {
10842  TimeOrderButton->Click();
10843  SetTopIndex(20);
10844  TimeOrderKeyFlag = false;
10845  }
10847  {
10848  TTServiceSyntaxCheckButton->Click();
10849  SetTopIndex(12);
10851  }
10852  else if(ValidateTimetableKeyFlag)
10853  {
10854  ValidateTimetableButton->Click();
10855  SetTopIndex(13);
10856  ValidateTimetableKeyFlag = false;
10857  }
10858  else if(SaveTTKeyFlag)
10859  {
10860  SaveTTButton->Click();
10861  SetTopIndex(14);
10862  SaveTTKeyFlag = false;
10863  }
10864  else if(SaveTTAsKeyFlag)
10865  {
10866  SaveTTAsButton->Click();
10867  SetTopIndex(15);
10868  SaveTTAsKeyFlag = false;
10869  }
10870  else if(RestoreTTKeyFlag)
10871  {
10872  RestoreTTButton->Click();
10873  SetTopIndex(16);
10874  RestoreTTKeyFlag = false;
10875  }
10876  else if(ExportTTKeyFlag)
10877  {
10878  ExportTTButton->Click();
10879  SetTopIndex(17);
10880  ExportTTKeyFlag = false;
10881  }
10882  else if(ConflictAnalysisKeyFlag)
10883  {
10884  ConflictAnalysisButton->Click();
10885  SetTopIndex(18);
10886  ConflictAnalysisKeyFlag = false;
10887  }
10888 // highlight timetable entry if in tt mode (have to call this regularly so will scroll with the listbox)
10889 //dropped at v2.13.0 as clicking an entry does all that is required
10890 /*
10891  if(!TimetableEditVector.empty() && (TTCurrentEntryIterator != TimetableEditVector.end()))
10892  {
10893  HighlightOneEntryInAllEntriesTTListBox(1, TTCurrentEntryIterator - TimetableEditVector.begin());
10894  }
10895  else
10896  {
10897  HighlightOneEntryInAllEntriesTTListBox(2, 0);
10898  }
10899 */
10900  }
10901 
10902 // set cursor
10904  {
10905  if(!TempCursorSet)
10906  {
10907  TempCursor = Screen->Cursor;
10908  TempCursorSet = true;
10909  }
10910  Screen->Cursor = TCursor(-11); // Hourglass
10911  }
10912  else
10913  {
10914  if(TempCursorSet)
10915  {
10916  Screen->Cursor = TempCursor;
10917  TempCursorSet = false;
10918  }
10919  }
10920  if(Level2OperMode == Operating)
10921  {
10922  TrainController->Operate(0); // ensure this called AFTER the single element route removal to ensure any single elements removed
10923  // prior to CallingOnAllowed being called (in UpdateTrain) as that sets a route from the stop signal
10924  if((TrainController->OpTimeToActUpdateCounter == 0) && (ActionsDueForm->Visible))
10925  {
10926  UpdateActionsDuePanel(0); // new at v2.2.0 to update panel when train OpTimeToAct updated (updated earlier)
10927  }
10928  TrainController->SignallerTrainRemovedOnAutoSigsRoute = false; // added at v1.3.0 to ensure doesn't persist beyond one call
10929  }
10930  else if((Level2OperMode == Paused) || (Level2OperMode == PreStart)) // added at v2.5.0 to show actions due after a session file reloaded
10931  { // modified at v2.10.0 to add PreStart
10932  THVShortPair ExitPair;
10933  ExitPair.first = -1;
10934  ExitPair.second = -1;
10935  if((TrainController->OpTimeToActUpdateCounter == 0) && (ActionsDueForm->Visible))
10936  {
10937  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
10938  {
10939  float LastTimeToExit = TrainController->TrainVectorAt(78, x).TimeToExit;
10940  TrainController->TrainVectorAt(73, x).OpTimeToAct = TrainController->TrainVectorAt(74, x).CalcTimeToAct(1, LastTimeToExit, ExitPair);
10941  TrainController->TrainVectorAt(79, x).TimeToExit = LastTimeToExit; //this was updated in CalcTimeToAct
10942  TrainController->TrainVectorAt(80, x).ExitPair = ExitPair;
10943  }
10946  }
10948  {
10950  }
10951  }
10952 
10953  // plot trains in ZoomOut mode & flash trains where attention needed alternately on & off at each call
10954 // by examining Flash
10955  if((Level1Mode == OperMode) && (Display->ZoomOutFlag))
10956  {
10958  }
10959 //deal with any manual LCs with barriers down in zoomout mode - these flash as reminder that need to re-open
10960 
10962  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++)
10963  {
10964  if((Track->BarriersDownVector.at(x).BarrierState == TTrack::Down) && (Track->BarriersDownVector.at(x).TypeOfRoute == 2))
10965  {
10966  //manual crossing down, but maybe a route across it
10967  bool TrainPresent; //not used outside function
10969  ConstructRoute->SearchVector, TrainPresent)) //no warning raised if a route or train present
10970  {
10973  {
10975  RailGraphics->smLC, Display); //smLC
10976  }
10977  else if((WarningFlashCount == 0) && !WarningFlash && Display->ZoomOutFlag)
10978  {
10980  RailGraphics->smSolidBgnd, Display); //SMOrange
10981  }
10982  }
10983  }
10984  }
10985 
10986 // Deal with any flashing graphics
10988  {
10989  FlashingGraphics(0, Now); // only call when WarningFlash changes
10990  if(Level1Mode == OperMode)
10991  {
10992  if(WarningFlash)
10993  {
10995  {
10996  CrashImage->Visible = true;
10997  }
10999  {
11000  DerailImage->Visible = true;
11001  }
11003  {
11004  SPADImage->Visible = true;
11005  }
11007  {
11008  TrainFailedImage->Visible = true;
11009  }
11011  {
11012  CallOnImage->Visible = true;
11013  }
11015  {
11016  SignalStopImage->Visible = true;
11017  }
11019  {
11020  BufferAttentionImage->Visible = true;
11021  }
11022  if(ManualLCDownAttentionWarning) //added at v2.9.0
11023  {
11024  ManualLCDownImage->Visible = true;
11025  }
11028  { //if any warnings visible then button is visible, else not
11029  FlashControlButton->Visible = true; //added at v2.15.0
11030  }
11031  else //this removes all when no warnings, as would stay visible if StopFlashFlag true
11032  {
11033  FlashControlButton->Visible = false; //added at v2.15.0
11034  DerailImage->Visible = false;
11035  SPADImage->Visible = false;
11036  TrainFailedImage->Visible = false;
11037  CallOnImage->Visible = false;
11038  SignalStopImage->Visible = false;
11039  BufferAttentionImage->Visible = false;
11040  ManualLCDownImage->Visible = false;
11041  }
11042  }
11043  else if(!StopFlashFlag)
11044  {
11045  CrashImage->Visible = false;
11046  DerailImage->Visible = false;
11047  SPADImage->Visible = false;
11048  TrainFailedImage->Visible = false;
11049  CallOnImage->Visible = false;
11050  SignalStopImage->Visible = false;
11051  BufferAttentionImage->Visible = false;
11052  ManualLCDownImage->Visible = false;
11053  }
11054  else
11055  {
11057  {
11058  CrashImage->Visible = false;
11059  }
11061  {
11062  DerailImage->Visible = false;
11063  }
11065  {
11066  SPADImage->Visible = false;
11067  }
11069  {
11070  TrainFailedImage->Visible = false;
11071  }
11073  {
11074  CallOnImage->Visible = false;
11075  }
11077  {
11078  SignalStopImage->Visible = false;
11079  }
11081  {
11082  BufferAttentionImage->Visible = false;
11083  }
11084  if(!ManualLCDownAttentionWarning) //added at v2.9.0
11085  {
11086  ManualLCDownImage->Visible = false;
11087  }
11088  }
11089  }
11090  else
11091  {
11092  CrashImage->Visible = false;
11093  DerailImage->Visible = false;
11094  SPADImage->Visible = false;
11095  TrainFailedImage->Visible = false;
11096  CallOnImage->Visible = false;
11097  SignalStopImage->Visible = false;
11098  BufferAttentionImage->Visible = false;
11100  FlashControlButton->Visible = false; //added at v2.15.0
11101  }
11102  } // if(WarningFlashCount == 0)
11103 
11104  // set buttons etc as appropriate
11106  // if forced route cancellation flag set redisplay to clear the cancelled route
11108  {
11110 // AllRoutes->RebuildRailwayFlag = false; //dropped at v2.14.0 & moved to ClearandRebuildRailway so it isn't called again if it is called before reaching this location
11111  }
11112  // deal with approach locking
11114  // deal with ContinuationAutoSigList
11116  // FloatingLabel function
11118  {
11119  TrackTrainFloat(0);
11120  }
11121  else
11122  {
11123  FloatingPanel->Visible = false;
11124  }
11125  // PerformanceLog check function
11126 
11128  {
11129  PerfLogForm->Visible = false;
11130  }
11131  else
11132  {
11133  if(ShowPerfLogForm)
11134  {
11135  PerfLogForm->Visible = true;
11136  }
11137  else
11138  {
11139  PerfLogForm->Visible = false;
11140  }
11141  }
11143  {
11144  ActionsDueForm->Visible = false;
11145  }
11146  else
11147  {
11148  if(ShowActionsDueForm)
11149  {
11150  if(ActionsDueForm->Visible == false)
11151  {
11152  ActionsDueForm->Top = ADFTop;
11153  ActionsDueForm->Left = ADFLeft;
11154  }
11155  ActionsDueForm->Visible = true;
11156  }
11157  else
11158  {
11159  ActionsDueForm->Visible = false;
11160  }
11161  }
11162 
11163  // check if a moving train is present on a route-under-construction start element & cancel it if so
11164  if(RouteMode == RouteContinuing)
11165  {
11166  bool FoundFlag;
11167  int RouteStartVecPos;
11168  if(AutoSigsFlag)
11169  {
11171  FoundFlag);
11172  }
11173  else if(PreferredRoute) // added at v2.7.0, was ConsecSignalsRoute
11174  {
11175  RouteStartVecPos = Track->GetVectorPositionFromTrackMap(8, (SigRouteStartMarker->GetHPos()) / 16, (SigRouteStartMarker->GetVPos()) / 16,
11176  FoundFlag);
11177  }
11178  else
11179  {
11181  FoundFlag);
11182  }
11183  if(FoundFlag && (RouteStartVecPos > -1))
11184  {
11185  TTrackElement TrackElement = Track->TrackElementAt(485, RouteStartVecPos);
11186  if(TrackElement.TrainIDOnElement > -1)
11187  {
11188  if(!(TrainController->TrainVectorAtIdent(2, TrackElement.TrainIDOnElement).Stopped()))
11189  {
11191  // replot train as above erases the front element of the train
11193  }
11194  }
11195  }
11196  }
11197  Utilities->CallLogPop(81);
11198  }
11199  catch(const EIdException &e) //non-error catch
11200 //if no response from peer then get a 'connection reset by peer' message which isn't valid
11201  {
11202 // ShowMessage(EIdExceptionSource + " " + e.Message); //<--temporary - remove this message eventually
11203  Utilities->CallLogPop(2406);
11204  }
11205  catch(const Exception &e)
11206  {
11207  ErrorLog(25, e.Message);
11208  }
11209 }
11210 
11211 // ---------------------------------------------------------------------------
11212 
11213 void __fastcall TInterface::CallingOnButtonClick(TObject *Sender)
11214 {
11215  try
11216  {
11217  TrainController->LogEvent("CallingOnButtonClick");
11218  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CallingOnButtonClick");
11219  if(CallingOnButton->Down)
11220  {
11221  // CallingOnButton->Down = true;
11222  InfoPanel->Visible = true;
11223  InfoPanel->Caption = "CALLING ON: Select signal for call on";
11224  }
11225  else
11226  {
11227  // CallingOnButton->Down = false;
11229  }
11230  AutoRouteStartMarker->PlotOriginal(29, Display); // if overlay not plotted will ignore
11231  SigRouteStartMarker->PlotOriginal(30, Display); // if overlay not plotted will ignore
11232  NonSigRouteStartMarker->PlotOriginal(31, Display); // if overlay not plotted will ignore
11233  CallingOnButton->Enabled = false;
11234 // added at v1.3.0 to ensure doesn't retain focus - will be re-enabled during ClockTimer2 (in SetSaveMenuAndButtons) if required
11235  Utilities->CallLogPop(82);
11236  }
11237  catch(const Exception &e)
11238  {
11239  ErrorLog(26, e.Message);
11240  }
11241 }
11242 
11243 // ---------------------------------------------------------------------------
11244 void __fastcall TInterface::ScreenLeftButtonClick(TObject *Sender)
11245 {
11246  try
11247  {
11248  // have to allow in zoomout mode
11249  TrainController->LogEvent("ScreenLeftButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11250  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ScreenLeftButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11251  Screen->Cursor = TCursor(-11); // Hourglass;
11252  ScreenLeftButton->Enabled = false; // to make multiple key presses less likely (not entirely successful)
11253  if(!Display->ZoomOutFlag)
11254  {
11255  if(CtrlKey)
11256  {
11257  Display->DisplayOffsetH -= 2;
11258  }
11259  else if(ShiftKey)
11260  {
11262  }
11263  else
11264  {
11266  }
11269  {
11271  }
11272  }
11273  else
11274  {
11275  if(CtrlKey)
11276  {
11278  }
11279  else if(ShiftKey)
11280  {
11282  }
11283  else
11284  {
11286  }
11287  Display->ClearDisplay(0);
11290  {
11291  Track->PlotSmallRedGap(0);
11292  }
11293  }
11294  ScreenLeftButton->Enabled = true;
11295  Screen->Cursor = TCursor(-2); // Arrow
11296  Utilities->CallLogPop(83);
11297  }
11298  catch(const Exception &e)
11299  {
11300  ErrorLog(27, e.Message);
11301  }
11302 }
11303 // ---------------------------------------------------------------------------
11304 
11305 void __fastcall TInterface::ScreenRightButtonClick(TObject *Sender)
11306 {
11307  try
11308  {
11309  // have to allow in zoomout mode
11310  TrainController->LogEvent("ScreenRightButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11311  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ScreenRightButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11312  Screen->Cursor = TCursor(-11); // Hourglass;
11313  ScreenRightButton->Enabled = false; // to make multiple key presses less likely (not entirely successful)
11314  if(!Display->ZoomOutFlag)
11315  {
11316  if(CtrlKey)
11317  {
11318  Display->DisplayOffsetH += 2;
11319  }
11320  else if(ShiftKey)
11321  {
11323  }
11324  else
11325  {
11327  }
11330  {
11332  }
11333  }
11334  else
11335  {
11336  if(CtrlKey)
11337  {
11339  }
11340  else if(ShiftKey)
11341  {
11343  }
11344  else
11345  {
11347  }
11348  Display->ClearDisplay(1);
11351  {
11352  Track->PlotSmallRedGap(1);
11353  }
11354  }
11355  ScreenRightButton->Enabled = true;
11356  Screen->Cursor = TCursor(-2); // Arrow
11357  Utilities->CallLogPop(84);
11358  }
11359  catch(const Exception &e)
11360  {
11361  ErrorLog(28, e.Message);
11362  }
11363 }
11364 // ---------------------------------------------------------------------------
11365 
11366 void __fastcall TInterface::ScreenDownButtonClick(TObject *Sender)
11367 {
11368  try
11369  {
11370  // have to allow in zoomout mode
11371  TrainController->LogEvent("ScreenDownButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11372  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ScreenDownButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11373  Screen->Cursor = TCursor(-11); // Hourglass;
11374  ScreenDownButton->Enabled = false; // to make multiple key presses less likely (not entirely successful)
11375  // BUT - it does prevent it from retaining focus - so can use the cursor keys to scroll the display without being captured by the buttons
11376  if(!Display->ZoomOutFlag)
11377  {
11378  if(CtrlKey)
11379  {
11380  Display->DisplayOffsetV += 2;
11381  }
11382  else if(ShiftKey)
11383  {
11385  }
11386  else
11387  {
11389  }
11392  {
11394  }
11395  }
11396  else
11397  {
11398  if(CtrlKey)
11399  {
11401  }
11402  else if(ShiftKey)
11403  {
11405  }
11406  else
11407  {
11409  }
11410  Display->ClearDisplay(2);
11413  {
11414  Track->PlotSmallRedGap(2);
11415  }
11416  }
11417  ScreenDownButton->Enabled = true;
11418  Screen->Cursor = TCursor(-2); // Arrow
11419  Utilities->CallLogPop(85);
11420  }
11421  catch(const Exception &e)
11422  {
11423  ErrorLog(29, e.Message);
11424  }
11425 }
11426 // ---------------------------------------------------------------------------
11427 
11428 void __fastcall TInterface::ScreenUpButtonClick(TObject *Sender)
11429 {
11430  try
11431  {
11432  // have to allow in zoomout mode
11433  TrainController->LogEvent("ScreenUpButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11434  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ScreenUpButtonClick" + AnsiString((short)ShiftKey) + AnsiString((short)CtrlKey));
11435  Screen->Cursor = TCursor(-11); // Hourglass;
11436  ScreenUpButton->Enabled = false; // to make multiple key presses less likely (not entirely successful)
11437  if(!Display->ZoomOutFlag)
11438  {
11439  if(CtrlKey)
11440  {
11441  Display->DisplayOffsetV -= 2;
11442  }
11443  else if(ShiftKey)
11444  {
11446  }
11447  else
11448  {
11450  }
11453  {
11455  }
11456  }
11457  else
11458  {
11459  if(CtrlKey)
11460  {
11462  }
11463  else if(ShiftKey)
11464  {
11466  }
11467  else
11468  {
11470  }
11471  Display->ClearDisplay(3);
11474  {
11475  Track->PlotSmallRedGap(3);
11476  }
11477  }
11478  ScreenUpButton->Enabled = true;
11479  Screen->Cursor = TCursor(-2); // Arrow
11480  Utilities->CallLogPop(86);
11481  }
11482  catch(const Exception &e)
11483  {
11484  ErrorLog(30, e.Message);
11485  }
11486 }
11487 // ---------------------------------------------------------------------------
11488 
11489 void __fastcall TInterface::ZoomButtonClick(TObject *Sender)
11490 {
11491  try
11492  {
11493  // have to allow in zoomout mode
11494  TrainController->LogEvent("ZoomButtonClick");
11495  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ZoomButtonClick");
11496  Screen->Cursor = TCursor(-11); // Hourglass;
11497  ZoomButton->Enabled = false; // this takes focus away so the arrow keys can move the display (v2.0.0)
11498  if(Display->ZoomOutFlag) // i.e resume zoomed in view
11499  {
11500  TrainController->LogEvent("ZoomButtonClick + ZoomOutFlag");
11501 // TLevel2OperMode TempLevel2OperMode = Level2OperMode;
11502  if(Level1Mode == BaseMode)
11503  {
11504  InfoPanel->Visible = false; // reset infopanel in case not set later
11505  InfoPanel->Caption = "";
11506  SetLevel1Mode(18);
11507  }
11508  else if(Level1Mode == TrackMode)
11509  {
11510  InfoPanel->Visible = false; // reset infopanel in case not set later
11511  InfoPanel->Caption = "";
11512  // set edit menu items
11514  SetLevel2TrackMode(33); // revert to earlier track mode from zoom
11515  }
11516  else if(Level1Mode == PrefDirMode)
11517  {
11519  {
11520  SetLevel1Mode(19); // to redisplay infopanel caption "...select start..."
11521  }
11522  else
11523  {
11524  SetLevel2PrefDirMode(4); // revert to PrefDirContinuing PrefDir mode
11525  }
11526  }
11527 // else if(Level1Mode == TrackMode) SetLevel1Mode();//just revert to basic track mode from zoom
11528 // else if(Level1Mode == PrefDirMode) SetLevel1Mode();//just revert to basic PrefDir mode from zoom
11529  else if(Level1Mode == TimetableMode)
11530  {
11531  InfoPanel->Visible = false;
11532  }
11533  // Don't include OperMode or RestartSessionOperMode as they reset the performance file
11534  else if(Level2OperMode == Operating) // similar to SetLevel2OperMode but without resetting BaseTime
11535  {
11536  OperateButton->Enabled = true;
11537  OperateButton->Glyph->LoadFromResourceName(0, "PauseGraphic");
11538  ExitOperationButton->Enabled = true;
11540  }
11541  else if(Level2OperMode == Paused) // similar to SetLevel2OperMode but without resetting RestartTime
11542  {
11543  OperateButton->Enabled = true;
11544  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
11545  ExitOperationButton->Enabled = true;
11546  TTClockAdjButton->Enabled = true;
11549  }
11550  else if(Level2OperMode == PreStart)
11551  {
11552  OperateButton->Enabled = true;
11553  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
11554  ExitOperationButton->Enabled = true;
11555  TTClockAdjButton->Enabled = true;
11557  }
11558  Display->ZoomOutFlag = false; // reset this after level modes called so gap flash stays set if set to begin with
11560  ClearandRebuildRailway(43); // need to call this after ZoomOutFlag reset to display track, even if Clearand... already called
11561  // earlier during level mode setting - because until ZoomOutFlag reset PlotOutput plots nothing
11562  }
11563  else // set zoomed out view
11564  {
11565  TrainController->LogEvent("ZoomButtonClick + != ZoomOutFlag");
11566  Display->ZoomOutFlag = true;
11568  FileMenu->Enabled = false;
11569  ModeMenu->Enabled = false;
11570  EditMenu->Enabled = false;
11571  TextBox->Visible = false;
11572  LocationNameTextBox->Visible = false;
11573  TTClockAdjButton->Enabled = false;
11574 // DisablePanelsStoreMainMenuStates();//ensure Display->ZoomOutFlag set true before calling
11575  // start assuming normal view is at centre of ZoomOut & calc excesses at each side
11576  int OVOffH_NVCentre = Display->DisplayOffsetH - (1.5 * Utilities->ScreenElementWidth);
11577 // start zoomout centre at DisplayOffsetH + 30 - zoomout width/2 = -(1.5 * 60)
11578  int LeftExcess = OVOffH_NVCentre - Track->GetHLocMin();
11579  int RightExcess = Track->GetHLocMax() - OVOffH_NVCentre - ((4 * Utilities->ScreenElementWidth) - 1);
11580  if((LeftExcess > 0) && (RightExcess > 0))
11581  {
11582  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre;
11583  }
11584  else if((LeftExcess > 0) && (RightExcess <= 0))
11585  {
11586  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre + ((RightExcess) / (Utilities->ScreenElementWidth / 2)) *
11587  (Utilities->ScreenElementWidth / 2); // normalise to nearest screen
11588  }
11589  else if((LeftExcess <= 0) && (RightExcess > 0))
11590  {
11591  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre - ((LeftExcess) / (Utilities->ScreenElementWidth / 2)) * (Utilities->ScreenElementWidth / 2);
11592  }
11593  else
11594  {
11595  Display->DisplayZoomOutOffsetH = OVOffH_NVCentre; // no excess at either side, so display in centre
11596 
11597  }
11598  int OVOffV_NVCentre = Display->DisplayOffsetV - (1.5 * Utilities->ScreenElementHeight);
11599  int TopExcess = OVOffV_NVCentre - Track->GetVLocMin();
11600  int BotExcess = Track->GetVLocMax() - OVOffV_NVCentre - ((4 * Utilities->ScreenElementHeight) - 1);
11601  if((TopExcess > 0) && (BotExcess > 0))
11602  {
11603  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre;
11604  }
11605  else if((TopExcess > 0) && (BotExcess <= 0))
11606  {
11607  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre + ((BotExcess) / (Utilities->ScreenElementHeight / 2)) *
11608  (Utilities->ScreenElementHeight / 2); // normalise to nearest half screen
11609  }
11610  else if((TopExcess <= 0) && (BotExcess > 0))
11611  {
11612  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre - ((TopExcess) / (Utilities->ScreenElementHeight / 2)) * (Utilities->ScreenElementHeight / 2);
11613  }
11614  else
11615  {
11616  Display->DisplayZoomOutOffsetV = OVOffV_NVCentre; // no excess at either side, so display in centre
11617 
11618  }
11619  Display->ClearDisplay(4);
11623  {
11624  Track->PlotSmallRedGap(4);
11625  }
11626  ZoomButton->Glyph->LoadFromResourceName(0, "ZoomIn");
11627  }
11628  Screen->Cursor = TCursor(-2); // Arrow
11629  ZoomButton->Enabled = true; // restore, see above
11630  Utilities->CallLogPop(87);
11631  }
11632  catch(const Exception &e)
11633  {
11634  ErrorLog(31, e.Message);
11635  }
11636 }
11637 // ---------------------------------------------------------------------------
11638 
11639 void __fastcall TInterface::HomeButtonClick(TObject *Sender)
11640 {
11641  try
11642  {
11643  // have to allow in zoomout mode
11644  TrainController->LogEvent("HomeButtonClick");
11645  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",HomeButtonClick");
11646  Screen->Cursor = TCursor(-11); // Hourglass;
11647  HomeButton->Enabled = false; // this takes focus away so the arrow keys can move the display (v2.0.0)
11648  if(!Display->ZoomOutFlag) // zoomed in mode
11649  {
11650  TrainController->LogEvent("HomeButtonClick + zoomed in mode");
11654  {
11656  }
11657  }
11658  else
11659  {
11660  // zoomed out mode
11661  // start assuming normal view is at centre of ZoomOut & calc excesses at each side
11662  TrainController->LogEvent("HomeButtonClick + zoomed out mode");
11664  Display->ClearDisplay(9);
11667  {
11668  Track->PlotSmallRedGap(5);
11669  }
11670  }
11671  Screen->Cursor = TCursor(-2); // Arrow
11672  HomeButton->Enabled = true; // restore, see above
11673  Utilities->CallLogPop(88);
11674  }
11675  catch(const Exception &e)
11676  {
11677  ErrorLog(32, e.Message);
11678  }
11679 }
11680 
11681 // ---------------------------------------------------------------------------
11682 void __fastcall TInterface::NewHomeButtonClick(TObject *Sender)
11683 {
11684  try
11685  {
11686  TrainController->LogEvent("NewHomeButtonClick");
11687  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",NewHomeButtonClick");
11688  NewHomeButton->Enabled = false; // this takes focus away so the arrow keys can move the display (v2.0.0)
11689  if(!Display->ZoomOutFlag) // zoomed in mode
11690  {
11693  ResetChangedFileDataAndCaption(23, false); // false because no major changes made
11694  }
11695  else
11696  {
11699  }
11700  Utilities->CallLogPop(1188);
11701  NewHomeButton->Enabled = true; // restore, see above
11702  }
11703  catch(const Exception &e)
11704  {
11705  ErrorLog(174, e.Message);
11706  }
11707 }
11708 
11709 // ---------------------------------------------------------------------------
11710 void __fastcall TInterface::EditMenuClick(TObject *Sender)
11711 // added at v2.1.0 to allow CTRL+X, CTRL+C & CTRL+V in edit menu (see case BaseMode for more information)
11712 {
11713  try
11714  {
11715  CopyMenuItem->ShortCut = TextToShortCut("Ctrl+C");
11716  CutMenuItem->ShortCut = TextToShortCut("Ctrl+X");
11717  PasteMenuItem->ShortCut = TextToShortCut("Ctrl+V");
11718  }
11719  catch(const Exception &e)
11720  {
11721  ErrorLog(196, e.Message);
11722  }
11723 }
11724 
11725 // ---------------------------------------------------------------------------
11726 void __fastcall TInterface::SelectMenuItemClick(TObject *Sender)
11727 {
11728 // draw a rectangle with the left mouse button, enclosing whole 16 x 16 squares
11729  try
11730  {
11731  TrainController->LogEvent("SelectMenuItemClick");
11732  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SelectMenuItemClick");
11733  if(Level1Mode == TrackMode)
11734  {
11735  SelectionValid = false;
11736  ExitHeatmaps(); //added at v2.22.0 to avoid spectrum image showing with track element panel after selection made
11738  SetLevel2TrackMode(34);
11740  {
11741  ShowMessage("Please be aware when pasting that anything inside the pasted area will be overwritten.\n\nThis warning will not be shown again.");
11742  PasteWarningSentFlag = true;
11743  }
11744  }
11745  else if(Level1Mode == PrefDirMode)
11746  {
11749  }
11750  Utilities->CallLogPop(1189);
11751  }
11752  catch(const Exception &e)
11753  {
11754  ErrorLog(145, e.Message);
11755  }
11756 }
11757 
11758 // ---------------------------------------------------------------------------
11759 void __fastcall TInterface::ReselectMenuItemClick(TObject *Sender)
11760 {
11761  try
11762  {
11763  TrainController->LogEvent("ReselectMenuItemClick");
11764  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ReselectMenuItemClick");
11765  if((SelectBitmap->Height == 0) || (SelectBitmap->Width == 0))
11766  {
11767  Utilities->CallLogPop(1424);
11768  return;
11769  }
11770  int TLHCH = SelectBitmapHLoc;
11771  int TLHCV = SelectBitmapVLoc;
11772  int BRHCH = TLHCH + (SelectBitmap->Width / 16);
11773  int BRHCV = TLHCV + (SelectBitmap->Height / 16);
11774  TRect NewSelectRect(TLHCH, TLHCV, BRHCH, BRHCV);
11775  SelectRect = NewSelectRect;
11777  // set bitmap to reselected area (may be different if flip or mirror had been selected earlier)
11778  TRect Dest(0, 0, SelectBitmap->Width, SelectBitmap->Height);
11779  TRect Source(((SelectRect.left - Display->DisplayOffsetH) * 16), ((SelectRect.top - Display->DisplayOffsetV) * 16),
11780  ((SelectRect.right - Display->DisplayOffsetH) * 16), ((SelectRect.bottom - Display->DisplayOffsetV) * 16));
11781  SelectBitmap->Canvas->CopyRect(Dest, MainScreen->Canvas, Source);
11782 
11783  SelectionValid = true;
11784  ReselectMenuItem->Enabled = false;
11785  CutMenuItem->Enabled = true;
11786  CopyMenuItem->Enabled = true;
11787  FlipMenuItem->Enabled = true;
11788  MirrorMenuItem->Enabled = true;
11789  RotRightMenuItem->Enabled = true;
11790  RotLeftMenuItem->Enabled = true;
11791  RotateMenuItem->Enabled = true;
11792  PasteMenuItem->Enabled = false;
11793  DeleteMenuItem->Enabled = true;
11794  if(Track->IsTrackFinished())
11795  {
11796  SelectLengthsMenuItem->Enabled = true; // only permit if finished because reverts to DistanceStart
11797  }
11798  else
11799  {
11800  SelectLengthsMenuItem->Enabled = false; // and that can only be used if track linked
11801  }
11802  SelectBiDirPrefDirsMenuItem->Visible = false;
11803  CheckPrefDirConflictsMenuItem->Visible = false;
11804  CancelSelectionMenuItem->Enabled = true;
11805  mbLeftDown = false;
11806  // Level1Mode = TrackMode;
11807  // SetLevel1Mode(68);
11809  SetLevel2TrackMode(47);
11810  Utilities->CallLogPop(1425);
11811  }
11812  catch(const Exception &e)
11813  {
11814  ErrorLog(146, e.Message);
11815  }
11816 }
11817 
11818 // ---------------------------------------------------------------------------
11819 void __fastcall TInterface::CutMenuItemClick(TObject *Sender)
11820 {
11821  try
11822  {
11823  TrainController->LogEvent("CutMenuItemClick");
11824  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CutMenuItemClick");
11825  // Level1Mode = TrackMode;
11826  // SetLevel1Mode(69);
11827  CopySelected = false; // new at v2.8.0
11828  LoadClipboard(0); // new at v2.8.0
11830  SetLevel2TrackMode(35);
11831  Utilities->CallLogPop(1190);
11832  }
11833  catch(const Exception &e)
11834  {
11835  ErrorLog(147, e.Message);
11836  }
11837 }
11838 // ---------------------------------------------------------------------------
11839 
11840 void __fastcall TInterface::CopyMenuItemClick(TObject *Sender)
11841 {
11842  try
11843  {
11844  TrainController->LogEvent("CopyMenuItemClick");
11845  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CopyMenuItemClick");
11846  // Level1Mode = TrackMode;
11847  // SetLevel1Mode(70);
11848  CopySelected = true; // new at v2.8.0
11849  LoadClipboard(1); // new at v2.8.0
11851  SetLevel2TrackMode(36);
11852  Utilities->CallLogPop(1191);
11853  }
11854  catch(const Exception &e)
11855  {
11856  ErrorLog(148, e.Message);
11857  }
11858 }
11859 
11860 // ---------------------------------------------------------------------------
11861 void __fastcall TInterface::FlipMenuItemClick(TObject *Sender)
11862 {
11863  try
11864  {
11865  TrainController->LogEvent("FlipMenuItemClick");
11866  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",FlipMenuItemClick");
11867  // reset values in SelectVector
11868  int VerSum = SelectRect.top + SelectRect.bottom - 1;
11869  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
11870  {
11871  // Note: (changed again in v2.4.0 to keep attributes) need to change flip, mirror & 180deg functions as only change speedtag without changing anything else.
11872  // This didn't matter before new paste with attributes added at v2.2.0 as a new element was built from the speedtag,
11873  // but now if do a reselect then cut and paste with attributes the wrong graphic is pasted and all other attributes
11874  // are wrong. Need to rebuild a new TrackElement from the new speedtag and use that in the select vector.
11875  // Note that if use Flip, mirror etc then all attributes lost anyway so ok to build a basic element.
11876  int VLoc = VerSum - Track->SelectVectorAt(8, x).VLoc;
11877  int HLoc = Track->SelectVectorAt(7, x).HLoc;
11879  TE.VLoc = VLoc;
11880  TE.HLoc = HLoc;
11881 
11882  TE.ActiveTrackElementName = Track->SelectVectorAt(37, x).ActiveTrackElementName; // these new in v2.4.0 so keeps attributes
11884  TE.Length01 = Track->SelectVectorAt(39, x).Length01;
11885  TE.Length23 = Track->SelectVectorAt(40, x).Length23;
11888  TE.SigAspect = Track->SelectVectorAt(43, x).SigAspect;
11889  Track->SelectVectorAt(26, x) = TE;
11890  }
11891 
11892  int FlipLinkArray[10] =
11893  {
11894  0, 7, 8, 9, 4, 5, 6, 1, 2, 3
11895  }; //0 & 5 are never used
11896 
11897  TrainController->LogEvent("Flip-track ok");
11898  // now reset the pref dirs
11899  for(unsigned int x = 0; x < SelectPrefDir->PrefDirSize(); x++)
11900  {
11901  int OriginalHLoc = SelectPrefDir->PrefDirVector.at(x).HLoc; //added at v2.9.1
11902  int OriginalVLoc = SelectPrefDir->PrefDirVector.at(x).VLoc;
11903  int VLoc = VerSum - SelectPrefDir->PrefDirVector.at(x).VLoc;
11904  int HLoc = SelectPrefDir->PrefDirVector.at(x).HLoc;
11905  int ELink = FlipLinkArray[SelectPrefDir->PrefDirVector.at(x).GetELink()];
11906  int XLink = FlipLinkArray[SelectPrefDir->PrefDirVector.at(x).GetXLink()];
11908  //the above line caused errors when Track->GetTrackElementFromAnyTrackMap in RecoverClipboard had Vector passed in by value,
11909  //causing a bad returned value for SpeedTag and other properties. Passing it in by reference fixed it
11910  TPrefDirElement PDE(TE); //this has Link[4]
11911  PDE.HLoc = HLoc;
11912  PDE.VLoc = VLoc;
11913  PDE.SetELink(ELink);
11914  PDE.SetXLink(XLink);
11915  bool ELinkPosFound = false, XLinkPosFound = false; //these ensure that the link pos is set as low as possible for points
11916  for(int y = 0; y < 4; y++) //changed to y at v2.9.1
11917  {
11918  if(!ELinkPosFound && (PDE.Link[y] == ELink))
11919  {
11920  PDE.SetELinkPos(y);
11921  ELinkPosFound = true;
11922  }
11923  if(!XLinkPosFound && (PDE.Link[y] == XLink))
11924  {
11925  PDE.SetXLinkPos(y);
11926  XLinkPosFound = true;
11927  }
11928  }
11929  //set the CheckCount as before - added at v2.9.1
11930  PDE.SetCheckCount(9); //explicitly set to 9 at v2.9.2
11933 
11934  //set the TrackVectorPosition to correspond to the corresponding TrackElement added at v2.9.1
11935  bool FoundFlag = false;
11936  PDE.SetTrackVectorPosition(Track->GetVectorPositionFromTrackMap(61, OriginalHLoc, OriginalVLoc, FoundFlag)); //uses original TV position as TrackMap hasn't changed yet
11937  if(PDE.GetSignedIntTrackVectorPosition() < 0)
11938  {
11939  FoundFlag = false; //probably will be anyway but reset to be sure & test below
11940  }
11942  if(!PDE.EntryExitNumber() || !ELinkPosFound || !XLinkPosFound || !FoundFlag) //error if can't set the number, any link pos not set or !FoundFlag
11943  {
11945  ShowMessage("Unable to re-orientate the preferred directions, these won't be set in the flipped selection");
11946  break;
11947  }
11948  SelectPrefDir->PrefDirVector.at(x) = PDE;
11949  }
11950 
11951  TrainController->LogEvent("Flip-prefdirs ok");
11952  // reset values in SelectTextVector
11953  for(unsigned int x = 0; x < TextHandler->SelectTextVectorSize(0); x++)
11954  {
11956  // also subtract font height, brings position approximately right
11957  TextItem->VPos = ((VerSum * 16) + 15) - TextItem->VPos - abs(TextItem->Font->Height);
11958  }
11959  TrainController->LogEvent("Flip-text ok");
11960  // reset values in SelectGraphicVector so the midpoint of the graphic flips about the midline of the selection
11961  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
11962  {
11963  int MidVPosBeforeFlip = Track->SelectGraphicVector.at(x).VPos + (Track->SelectGraphicVector.at(x).Height) / 2;
11964  int MidVPosAfterFlip = ((VerSum * 16) + 15) - MidVPosBeforeFlip;
11965  int TopPosAfterFlip = MidVPosAfterFlip - (Track->SelectGraphicVector.at(x).Height) / 2;
11966  Track->SelectGraphicVector.at(x).VPos = TopPosAfterFlip;
11967  }
11969  SetLevel2TrackMode(48);
11970  Utilities->CallLogPop(1426);
11971  }
11972  catch(const Exception &e)
11973  {
11974  ErrorLog(149, e.Message);
11975  }
11976 }
11977 
11978 // ---------------------------------------------------------------------------
11979 void __fastcall TInterface::MirrorMenuItemClick(TObject *Sender)
11980 {
11981  try
11982  {
11983  TrainController->LogEvent("MirrorMenuItemClick");
11984  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MirrorMenuItemClick");
11985  // reset values in SelectVector
11986  int HorSum = SelectRect.left + SelectRect.right - 1;
11987  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
11988  {
11989  // See note above for FlipMenuItem relating to mods for v2.2.0
11990  int VLoc = Track->SelectVectorAt(22, x).VLoc;
11991  int HLoc = HorSum - Track->SelectVectorAt(6, x).HLoc;
11993  TE.VLoc = VLoc;
11994  TE.HLoc = HLoc;
11995 
11996  TE.ActiveTrackElementName = Track->SelectVectorAt(44, x).ActiveTrackElementName; // these new in v2.4.0 so keeps attributes
11998  TE.Length01 = Track->SelectVectorAt(46, x).Length01;
11999  TE.Length23 = Track->SelectVectorAt(47, x).Length23;
12002  TE.SigAspect = Track->SelectVectorAt(50, x).SigAspect;
12003 
12004 // if(Track->SelectVectorAt(28, x).TrackType == SignalPost) TE.SigAspect = Track->SelectVectorAt(29, x).SigAspect;//TrackType will be the same
12005  Track->SelectVectorAt(30, x) = TE;
12006 // Track->SelectVectorAt(, x).HLoc = HorSum - Track->SelectVectorAt(, x).HLoc;
12007 // Track->SelectVectorAt(, x).SpeedTag = Track->MirrorArray[Track->SelectVectorAt(, x).SpeedTag];
12008  }
12009 
12010  int MirrorLinkArray[10] =
12011  {
12012  0, 3, 2, 1, 6, 5, 4, 9, 8, 7
12013  }; //0 & 5 are never used
12014 
12015  TrainController->LogEvent("Mirror-track ok");
12016  // now reset the pref dirs
12017  for(unsigned int x = 0; x < SelectPrefDir->PrefDirSize(); x++)
12018  {
12019  int OriginalHLoc = SelectPrefDir->PrefDirVector.at(x).HLoc; //added at v2.9.1
12020  int OriginalVLoc = SelectPrefDir->PrefDirVector.at(x).VLoc;
12021  int HLoc = HorSum - SelectPrefDir->PrefDirVector.at(x).HLoc;
12022  int VLoc = SelectPrefDir->PrefDirVector.at(x).VLoc;
12023  int ELink = MirrorLinkArray[SelectPrefDir->PrefDirVector.at(x).GetELink()];
12024  int XLink = MirrorLinkArray[SelectPrefDir->PrefDirVector.at(x).GetXLink()];
12026  //the above line caused errors when Track->GetTrackElementFromAnyTrackMap in RecoverClipboard had Vector passed in by value,
12027  //causing a bad returned value for SpeedTag and other properties. Passing it in by reference fixed it
12028  TPrefDirElement PDE(TE); //this has Link[4]
12029  PDE.HLoc = HLoc;
12030  PDE.VLoc = VLoc;
12031  PDE.SetELink(ELink);
12032  PDE.SetXLink(XLink);
12033  bool ELinkPosFound = false, XLinkPosFound = false; //these ensure that the link pos is set as low as possible for points
12034  for(int y = 0; y < 4; y++) //changed to y at v2.9.1
12035  {
12036  if(!ELinkPosFound && (PDE.Link[y] == ELink))
12037  {
12038  PDE.SetELinkPos(y);
12039  ELinkPosFound = true;
12040  }
12041  if(!XLinkPosFound && (PDE.Link[y] == XLink))
12042  {
12043  PDE.SetXLinkPos(y);
12044  XLinkPosFound = true;
12045  }
12046  }
12047  //set the CheckCount as before - added at v2.9.1
12048  PDE.SetCheckCount(9); //explicitly set to 9 at v2.9.2
12051  //set the TrackVectorPosition to correspond to the corresponding TrackElement added at v2.9.1
12052  bool FoundFlag = false;
12053  PDE.SetTrackVectorPosition(Track->GetVectorPositionFromTrackMap(62, OriginalHLoc, OriginalVLoc, FoundFlag)); //uses original TV position as TrackMap hasn't changed yet
12054  if(PDE.GetSignedIntTrackVectorPosition() < 0)
12055  {
12056  FoundFlag = false; //probably will be anyway but reset to be sure & test below
12057  }
12059  if(!PDE.EntryExitNumber() || !ELinkPosFound || !XLinkPosFound || !FoundFlag) //error if can't set the number, any link pos not set or !FoundFlag
12060  {
12062  ShowMessage("Unable to re-orientate the preferred directions, these won't be set in the mirrored selection");
12063  break;
12064  }
12065  SelectPrefDir->PrefDirVector.at(x) = PDE;
12066  }
12067 
12068  TrainController->LogEvent("Mirror-PDs ok");
12069  // reset values in SelectTextVector
12070  for(unsigned int x = 0; x < TextHandler->SelectTextVectorSize(1); x++)
12071  {
12073  // also subtract half font height for each letter of text, brings position approximately right
12074  TextItem->HPos = ((HorSum * 16) + 15) - TextItem->HPos - (TextItem->TextString.Length() * 0.5 * abs(TextItem->Font->Height));
12075  }
12076  TrainController->LogEvent("Mirror-text ok");
12077  // reset values in SelectGraphicVector so the midpoint of the graphic mirrors about the midline of the selection
12078  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
12079  {
12080  int MidHPosBeforeMirror = Track->SelectGraphicVector.at(x).HPos + (Track->SelectGraphicVector.at(x).Width) / 2;
12081  int MidHPosAfterMirror = ((HorSum * 16) + 15) - MidHPosBeforeMirror;
12082  int LeftPosAfterMirror = MidHPosAfterMirror - (Track->SelectGraphicVector.at(x).Width) / 2;
12083  if(LeftPosAfterMirror < (SelectRect.left * 16)) // shouldn't go below left but check
12084  {
12085  LeftPosAfterMirror = SelectRect.left * 16;
12086  }
12087  Track->SelectGraphicVector.at(x).HPos = LeftPosAfterMirror;
12088  }
12090  SetLevel2TrackMode(49);
12091  Utilities->CallLogPop(1427);
12092  }
12093  catch(const Exception &e)
12094  {
12095  ErrorLog(150, e.Message);
12096  }
12097 }
12098 
12099 // ---------------------------------------------------------------------------
12100 void __fastcall TInterface::RotateMenuItemClick(TObject *Sender)
12101 {
12102  try
12103  {
12104  TrainController->LogEvent("Rotate180MenuItemClick");
12105  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",Rotate180MenuItemClick");
12106 
12107  // reset values in SelectVector
12108  int HorSum = SelectRect.left + SelectRect.right - 1;
12109  int VerSum = SelectRect.top + SelectRect.bottom - 1;
12110  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
12111  {
12112  // See note above for FlipMenuItem relating to mods for v2.2.0
12113  int VLoc = VerSum - Track->SelectVectorAt(23, x).VLoc;
12114  int HLoc = HorSum - Track->SelectVectorAt(36, x).HLoc;
12116  TE.VLoc = VLoc;
12117  TE.HLoc = HLoc;
12118 
12119  TE.ActiveTrackElementName = Track->SelectVectorAt(51, x).ActiveTrackElementName; // these new in v2.4.0 so keeps attributes
12121  TE.Length01 = Track->SelectVectorAt(53, x).Length01;
12122  TE.Length23 = Track->SelectVectorAt(54, x).Length23;
12125  TE.SigAspect = Track->SelectVectorAt(57, x).SigAspect;
12126 
12127 // if(Track->SelectVectorAt(32, x).TrackType == SignalPost) TE.SigAspect = Track->SelectVectorAt(33, x).SigAspect; dropped in v2.4.0 for above
12128  Track->SelectVectorAt(34, x) = TE;
12129 // TTrackElement &TempEl = Track->SelectVectorAt(13, x);
12130 // TempEl.HLoc = HorSum - TempEl.HLoc;
12131 // TempEl.VLoc = VerSum - TempEl.VLoc;
12132 // TempEl.SpeedTag = Track->MirrorArray[Track->FlipArray[TempEl.SpeedTag]];
12133  }
12134 
12135  int Rot180LinkArray[10] =
12136  {
12137  0, 9, 8, 7, 6, 5, 4, 3, 2, 1
12138  }; //0 & 5 are never used
12139  TrainController->LogEvent("Rotate-track ok");
12140  // now reset the pref dirs
12141  for(unsigned int x = 0; x < SelectPrefDir->PrefDirSize(); x++)
12142  {
12143  int OriginalHLoc = SelectPrefDir->PrefDirVector.at(x).HLoc; //added at v2.9.1
12144  int OriginalVLoc = SelectPrefDir->PrefDirVector.at(x).VLoc;
12145  int HLoc = HorSum - SelectPrefDir->PrefDirVector.at(x).HLoc;
12146  int VLoc = VerSum - SelectPrefDir->PrefDirVector.at(x).VLoc;
12147  int ELink = Rot180LinkArray[SelectPrefDir->PrefDirVector.at(x).GetELink()];
12148  int XLink = Rot180LinkArray[SelectPrefDir->PrefDirVector.at(x).GetXLink()];
12150  //the above line caused errors when Track->GetTrackElementFromAnyTrackMap in RecoverClipboard had Vector passed in by value,
12151  //causing a bad returned value for SpeedTag and other properties. Passing it in by reference fixed it
12152  TPrefDirElement PDE(TE); //this has Link[4]
12153  PDE.HLoc = HLoc;
12154  PDE.VLoc = VLoc;
12155  PDE.SetELink(ELink);
12156  PDE.SetXLink(XLink);
12157  bool ELinkPosFound = false, XLinkPosFound = false; //these ensure that the link pos is set as low as possible for points
12158  for(int y = 0; y < 4; y++) //changed to y at v2.9.1
12159  {
12160  if(!ELinkPosFound && (PDE.Link[y] == ELink))
12161  {
12162  PDE.SetELinkPos(y);
12163  ELinkPosFound = true;
12164  }
12165  if(!XLinkPosFound && (PDE.Link[y] == XLink))
12166  {
12167  PDE.SetXLinkPos(y);
12168  XLinkPosFound = true;
12169  }
12170  }
12171  //set the CheckCount as before - added at v2.9.1
12172  PDE.SetCheckCount(9); //explicitly set to 9 at v2.9.2
12175  //set the TrackVectorPosition to correspond to the corresponding TrackElement added at v2.9.1
12176  bool FoundFlag = false;
12177  PDE.SetTrackVectorPosition(Track->GetVectorPositionFromTrackMap(63, OriginalHLoc, OriginalVLoc, FoundFlag)); //uses original TV position as TrackMap hasn't changed yet
12178  if(PDE.GetSignedIntTrackVectorPosition() < 0)
12179  {
12180  FoundFlag = false; //probably will be anyway but reset to be sure & test below
12181  }
12183  if(!PDE.EntryExitNumber() || !ELinkPosFound || !XLinkPosFound || !FoundFlag) //error if can't set the number, any link pos not set or !FoundFlag
12184  {
12186  ShowMessage("Unable to re-orientate the preferred directions, these won't be set in the rotated selection");
12187  break;
12188  }
12189  SelectPrefDir->PrefDirVector.at(x) = PDE;
12190  }
12191  TrainController->LogEvent("Rotate-PDs ok");
12192  // reset values in SelectTextVector
12193  for(unsigned int x = 0; x < TextHandler->SelectTextVectorSize(2); x++)
12194  {
12196  // also subtract half font height for each letter of text, brings position approximately right horizontally
12197  TextItem->HPos = ((HorSum * 16) + 15) - TextItem->HPos - (TextItem->TextString.Length() * 0.5 * abs(TextItem->Font->Height));
12198  // also subtract font height, brings position approximately right vertically
12199  TextItem->VPos = ((VerSum * 16) + 15) - TextItem->VPos - abs(TextItem->Font->Height);
12200  }
12201  TrainController->LogEvent("Rotate-text ok");
12202  // reset flip values in SelectGraphicVector so the midpoint of the graphic flips about the midline of the selection
12203  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
12204  {
12205  int MidVPosBeforeFlip = Track->SelectGraphicVector.at(x).VPos + (Track->SelectGraphicVector.at(x).Height) / 2;
12206  int MidVPosAfterFlip = ((VerSum * 16) + 15) - MidVPosBeforeFlip;
12207  int TopPosAfterFlip = MidVPosAfterFlip - (Track->SelectGraphicVector.at(x).Height) / 2;
12208  if(TopPosAfterFlip < (SelectRect.top * 16)) // shouldn't go above top but check
12209  {
12210  TopPosAfterFlip = SelectRect.top * 16;
12211  }
12212  Track->SelectGraphicVector.at(x).VPos = TopPosAfterFlip;
12213  }
12214  // reset mirror in SelectGraphicVector so the midpoint of the graphic mirrors about the midline of the selection
12215  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
12216  {
12217  int MidHPosBeforeMirror = Track->SelectGraphicVector.at(x).HPos + (Track->SelectGraphicVector.at(x).Width) / 2;
12218  int MidHPosAfterMirror = ((HorSum * 16) + 15) - MidHPosBeforeMirror;
12219  int LeftPosAfterMirror = MidHPosAfterMirror - (Track->SelectGraphicVector.at(x).Width) / 2;
12220  if(LeftPosAfterMirror < (SelectRect.left * 16)) // shouldn't go below left but check
12221  {
12222  LeftPosAfterMirror = SelectRect.left * 16;
12223  }
12224  Track->SelectGraphicVector.at(x).HPos = LeftPosAfterMirror;
12225  }
12226  // Level1Mode = TrackMode;
12227  // SetLevel1Mode(73);
12229  SetLevel2TrackMode(50);
12230  Utilities->CallLogPop(1435);
12231  }
12232  catch(const Exception &e)
12233  {
12234  ErrorLog(151, e.Message);
12235  }
12236 }
12237 // ---------------------------------------------------------------------------
12238 
12239 void __fastcall TInterface::RotRightMenuItemClick(TObject *Sender)
12240 {
12241  try
12242  {
12243  TrainController->LogEvent("RotateRightMenuItemClick");
12244  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RotateRightMenuItemClick");
12245  Screen->Cursor = TCursor(-11); // Hourglass
12246  // check first if a square and if not give message & quit
12247  if((SelectRect.right - SelectRect.left) != (SelectRect.bottom - SelectRect.top))
12248  {
12249  // use left vertical side to make square & keep top lh corner unless rhs would exceed display, in which case use the right vertical & keep to rh corner
12250  int VertSize = SelectRect.bottom - SelectRect.top;
12251  if((SelectRect.left + VertSize - Display->DisplayOffsetH) > Utilities->ScreenElementWidth)
12252  {
12253  // use right hand vertical & make square to left of that
12254  SelectRect.left = SelectRect.right - VertSize;
12255  }
12256  else
12257  {
12258  SelectRect.right = SelectRect.left + VertSize;
12259  }
12262  int button = Application->MessageBox
12263  (L"Original selection adjusted to make it square. 'OK' to keep this selection or 'Cancel' to make a new selection",
12264  L"Left click and hold here to move this message box", MB_OKCANCEL);
12265  if(button == IDCANCEL)
12266  {
12267  ResetSelectRect();
12268  Level1Mode = TrackMode; // call this first to clear everything, then set PrefDir mode
12269  SetLevel1Mode(133);
12271  SetLevel2TrackMode(59);
12273  Screen->Cursor = TCursor(-2); // Arrow
12274  Utilities->CallLogPop(2121);
12275  return;
12276  }
12277  }
12278  // set SelectBitmap (only need the dimensions here as not moving the selection)
12281  SelectBitmap->Width = (SelectRect.right - SelectRect.left) * 16;
12282  SelectBitmap->Height = (SelectRect.bottom - SelectRect.top) * 16;
12283 
12284  // store track elements and text in select vectors - have to store here because original selection might well have changed
12286  TTrackElement TempElement; // default element
12287  bool FoundFlag;
12288  for(int x = SelectRect.left; x < SelectRect.right; x++)
12289  {
12290  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12291  {
12292  int ATVecPos = Track->GetVectorPositionFromTrackMap(57, x, y, FoundFlag);
12293  if(FoundFlag)
12294  {
12295  TempElement = Track->TrackElementAt(959, ATVecPos);
12296  if(TempElement.SpeedTag > 0)
12297  {
12298  Track->SelectPush(TempElement);
12299  }
12300  }
12301  }
12302  }
12303  // now store inactive elements
12304  for(int x = SelectRect.left; x < SelectRect.right; x++)
12305  {
12306  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12307  {
12308  TTrack::TIMPair IATVecPair = Track->GetVectorPositionsFromInactiveTrackMap(28, x, y, FoundFlag);
12309  if(FoundFlag)
12310  {
12311  TempElement = Track->InactiveTrackElementAt(126, IATVecPair.first);
12312  Track->SelectPush(TempElement); // only want SpeedTag & location set, rest defaults
12313  if(IATVecPair.second != IATVecPair.first) // 2 elements stored at location, i.e. platforms
12314  {
12315  TempElement = Track->InactiveTrackElementAt(127, IATVecPair.second);
12316  Track->SelectPush(TempElement);
12317  }
12318  }
12319  }
12320  }
12321  TrainController->LogEvent("RotRight-trackstore ok");
12322  //store preferred directions //added at v2.9.0
12323  int PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3;
12324  TPrefDirElement TempPrefDirElement;
12326  for(int x = SelectRect.left; x < SelectRect.right; x++)
12327  {
12328  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12329  {
12330  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(12, x, y, FoundFlag, PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3);
12331  if(FoundFlag)
12332  {
12333  if(PrefDirPos0 > -1)
12334  {
12335  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(6, PrefDirPos0);
12336  SelectPrefDir->ExternalStorePrefDirElement(12, TempPrefDirElement);
12337  }
12338  if(PrefDirPos1 > -1)
12339  {
12340  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(7, PrefDirPos1);
12341  SelectPrefDir->ExternalStorePrefDirElement(13, TempPrefDirElement);
12342  }
12343  if(PrefDirPos2 > -1)
12344  {
12345  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(8, PrefDirPos2);
12346  SelectPrefDir->ExternalStorePrefDirElement(14, TempPrefDirElement);
12347  }
12348  if(PrefDirPos3 > -1)
12349  {
12350  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(9, PrefDirPos3);
12351  SelectPrefDir->ExternalStorePrefDirElement(15, TempPrefDirElement);
12352  }
12353  }
12354  }
12355  }
12356  TrainController->LogEvent("RotRight-PDstore ok");
12357  // store text items
12358  int LowSelectHPos = SelectRect.left * 16;
12359  int HighSelectHPos = SelectRect.right * 16;
12360  int LowSelectVPos = SelectRect.top * 16;
12361  int HighSelectVPos = SelectRect.bottom * 16;
12362  TextHandler->SelectTextVector.clear();
12363  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
12364  {
12365  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr < TextHandler->TextVector.end(); TextPtr++)
12366  {
12367  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos < HighSelectVPos))
12368  {
12369  // have to create a new TextItem in order to create a new Font object
12370  // BUT: only create new items where they don't appear as named location names
12371  // in SelectVector, since those names shouldn't be copied or pasted.
12372  // NB: altered for PasteWithAttributes - at v2.2.0 save the named element but prefix it with "##**"
12373  // so can paste or not depending on which type of paste is being used (unlikely to use that in a real name)
12374  bool SelectVectorNamedElement = false;
12375  AnsiString SelectTextString; // new at v2.2.0
12376  for(unsigned int x = 0; x < Track->SelectVector.size(); x++)
12377  {
12378  if(Track->SelectVector.at(x).LocationName == TextPtr->TextString)
12379  {
12380  SelectVectorNamedElement = true;
12381  break;
12382  }
12383  }
12384  if(SelectVectorNamedElement) // changed at v2.2.0
12385  {
12386  SelectTextString = "##**" + TextPtr->TextString; // new at v2.2.0
12387  }
12388  else // new at v2.2.0
12389  {
12390  SelectTextString = TextPtr->TextString;
12391  }
12392  TTextItem TextItem(TextPtr->HPos, TextPtr->VPos, SelectTextString, TextPtr->Font);
12393  TextHandler->SelectTextVector.push_back(TextItem); // changed at v2.2.0
12394  }
12395  }
12396  }
12397  TrainController->LogEvent("RotRight-textstore ok");
12398  // store graphic items, but first clear SelectGraphicVector
12399  Track->SelectGraphicVector.clear();
12400  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
12401  {
12402  for(TTrack::TUserGraphicVector::iterator UserGraphicPtr = Track->UserGraphicVector.begin(); UserGraphicPtr < Track->UserGraphicVector.end();
12403  UserGraphicPtr++)
12404  {
12405  if((UserGraphicPtr->HPos >= LowSelectHPos) && ((UserGraphicPtr->HPos + UserGraphicPtr->Width) < HighSelectHPos) && (UserGraphicPtr->VPos >=
12406  LowSelectVPos) && ((UserGraphicPtr->VPos + UserGraphicPtr->Height) < HighSelectVPos))
12407  {
12408  Track->SelectGraphicVector.push_back(*UserGraphicPtr);
12409  }
12410  }
12411  }
12412  TrainController->LogEvent("RotRight-graphicstore ok");
12413  // now transform the H & V for rh rotate
12414  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
12415  {
12416  int HLoc = SelectRect.bottom - 1 + SelectRect.left - Track->SelectVectorAt(74, x).VLoc;
12417  int VLoc = SelectRect.top - SelectRect.left + Track->SelectVectorAt(75, x).HLoc;
12419  TE.VLoc = VLoc;
12420  TE.HLoc = HLoc;
12421 
12422  TE.ActiveTrackElementName = Track->SelectVectorAt(58, x).ActiveTrackElementName; // these new in v2.4.0 so keeps attributes
12424  TE.Length01 = Track->SelectVectorAt(60, x).Length01;
12425  TE.Length23 = Track->SelectVectorAt(61, x).Length23;
12428  TE.SigAspect = Track->SelectVectorAt(64, x).SigAspect;
12429  Track->SelectVectorAt(65, x) = TE;
12430  }
12431 
12432  int RotRightLinkArray[10] =
12433  {
12434  0, 3, 6, 9, 2, 5, 8, 1, 4, 7
12435  }; //0 & 5 are never used
12436  TrainController->LogEvent("RotRight-trackrotate ok");
12437  // now transform the pref dirs
12438  for(unsigned int x = 0; x < SelectPrefDir->PrefDirSize(); x++)
12439  {
12440  int OriginalHLoc = SelectPrefDir->GetFixedPrefDirElementAt(250, x).HLoc; //added at v2.9.1
12441  int OriginalVLoc = SelectPrefDir->GetFixedPrefDirElementAt(251, x).VLoc;
12442  int HLoc = SelectRect.bottom - 1 + SelectRect.left - OriginalVLoc;
12443  int VLoc = SelectRect.top - SelectRect.left + OriginalHLoc;
12444  int ELink = RotRightLinkArray[SelectPrefDir->GetFixedPrefDirElementAt(252, x).GetELink()];
12445  int XLink = RotRightLinkArray[SelectPrefDir->GetFixedPrefDirElementAt(253, x).GetXLink()];
12447  //the above line caused errors when Track->GetTrackElementFromAnyTrackMap in RecoverClipboard had Vector passed in by value,
12448  //causing a bad returned value for SpeedTag and other properties. Passing it in by reference fixed it
12449  TPrefDirElement PDE(TE); //this has Link[4]
12450  PDE.HLoc = HLoc;
12451  PDE.VLoc = VLoc;
12452  PDE.SetELink(ELink);
12453  PDE.SetXLink(XLink);
12454  bool ELinkPosFound = false, XLinkPosFound = false; //these ensure that the link pos is set as low as possible for points
12455  for(int y = 0; y < 4; y++) //changed to y at v2.9.1
12456  {
12457  if(!ELinkPosFound && (PDE.Link[y] == ELink))
12458  {
12459  PDE.SetELinkPos(y);
12460  ELinkPosFound = true;
12461  }
12462  if(!XLinkPosFound && (PDE.Link[y] == XLink))
12463  {
12464  PDE.SetXLinkPos(y);
12465  XLinkPosFound = true;
12466  }
12467  }
12468  //set the CheckCount as before - added at v2.9.1
12469  PDE.SetCheckCount(9); //explicitly set to 9 at v2.9.2
12472  //set the TrackVectorPosition to correspond to the corresponding TrackElement added at v2.9.1
12473  bool FoundFlag = false;
12474  PDE.SetTrackVectorPosition(Track->GetVectorPositionFromTrackMap(64, OriginalHLoc, OriginalVLoc, FoundFlag)); //uses original TV position as TrackMap hasn't changed yet
12475  if(PDE.GetSignedIntTrackVectorPosition() < 0)
12476  {
12477  FoundFlag = false; //probably will be anyway but reset to be sure & test below
12478  }
12480  if(!PDE.EntryExitNumber() || !ELinkPosFound || !XLinkPosFound || !FoundFlag) //error if can't set the number, any link pos not set or !FoundFlag
12481  {
12483  ShowMessage("Unable to re-orientate the preferred directions, these won't be set in the rotated selection");
12484  break;
12485  }
12486  SelectPrefDir->PrefDirVector.at(x) = PDE;
12487  }
12488  TrainController->LogEvent("RotRight-PDrotate ok");
12489  // reset values in SelectTextVector
12490  for(unsigned int x = 0; x < TextHandler->SelectTextVectorSize(3); x++)
12491  {
12492 // no point trying to locate text properly as it stays horizontal so will always be wrongly placed, just list all itels vertically at lhs
12493 // & if a lot then some will extend beyond the selection
12495  // also subtract half font height for each letter of text, brings position approximately right horizontally
12496  TextItem->HPos = (SelectRect.left) * 16;
12497  TextItem->VPos = (SelectRect.top + x) * 16;
12498  }
12499  TrainController->LogEvent("RotRight-textrotate ok");
12500  // reset values in SelectGraphicVector
12501  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
12502  {
12503  int MidHPosBeforeRotate = Track->SelectGraphicVector.at(x).HPos + Track->SelectGraphicVector.at(x).Width / 2;
12504  int MidVPosBeforeRotate = Track->SelectGraphicVector.at(x).VPos + Track->SelectGraphicVector.at(x).Height / 2;
12505  int MidHPosAfterRotate = ((SelectRect.bottom * 16) - 1) + (SelectRect.left * 16) - MidVPosBeforeRotate;
12506  int MidVPosAfterRotate = ((SelectRect.top - SelectRect.left) * 16) + MidHPosBeforeRotate;
12507  int LeftPosAfterRotate = MidHPosAfterRotate - (Track->SelectGraphicVector.at(x).Width) / 2;
12508  int TopPosAfterRotate = MidVPosAfterRotate - (Track->SelectGraphicVector.at(x).Height) / 2;
12509  Track->SelectGraphicVector.at(x).HPos = LeftPosAfterRotate;
12510  Track->SelectGraphicVector.at(x).VPos = TopPosAfterRotate;
12511  }
12512  Screen->Cursor = TCursor(-2); // Arrow
12514  SetLevel2TrackMode(60);
12515  Utilities->CallLogPop(2122);
12516  }
12517  catch(const Exception &e)
12518  {
12519  ErrorLog(205, e.Message);
12520  }
12521 }
12522 
12523 // ---------------------------------------------------------------------------
12524 
12525 void __fastcall TInterface::RotLeftMenuItemClick(TObject *Sender)
12526 {
12527  try
12528  {
12529  TrainController->LogEvent("RotateLeftMenuItemClick");
12530  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RotateLeftMenuItemClick");
12531  Screen->Cursor = TCursor(-11); // Hourglass;
12532  // check first if a square and if not give message & quit
12533  if((SelectRect.right - SelectRect.left) != (SelectRect.bottom - SelectRect.top))
12534  {
12535  // use left vertical side to make square & keep top lh corner unless rhs would exceed display, in which case use the right vertical & keep to rh corner
12536  int VertSize = SelectRect.bottom - SelectRect.top;
12537  if((SelectRect.left + VertSize - Display->DisplayOffsetH) > Utilities->ScreenElementWidth)
12538  {
12539  // use right hand vertical & make square to left of that
12540  SelectRect.left = SelectRect.right - VertSize;
12541  }
12542  else
12543  {
12544  SelectRect.right = SelectRect.left + VertSize;
12545  }
12548  int button = Application->MessageBox
12549  (L"Original selection adjusted to make it square. 'OK' to keep this selection or 'Cancel' to make a new selection",
12550  L"Left click and hold here to move this message box", MB_OKCANCEL);
12551  if(button == IDCANCEL)
12552  {
12553  ResetSelectRect();
12554  Level1Mode = TrackMode; // call this first to clear everything, then set PrefDir mode
12555  SetLevel1Mode(134);
12557  SetLevel2TrackMode(61);
12559  Screen->Cursor = TCursor(-2); // Arrow
12560  Utilities->CallLogPop(2123);
12561  return;
12562  }
12563  }
12564  // set SelectBitmap (only need the dimensions here as not moving the selection)
12567  SelectBitmap->Width = (SelectRect.right - SelectRect.left) * 16;
12568  SelectBitmap->Height = (SelectRect.bottom - SelectRect.top) * 16;
12569 
12570  // store track elements and text in select vectors - have to store here because original selection might well have changed
12572  TTrackElement TempElement; // default element
12573  bool FoundFlag;
12574  for(int x = SelectRect.left; x < SelectRect.right; x++)
12575  {
12576  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12577  {
12578  int ATVecPos = Track->GetVectorPositionFromTrackMap(58, x, y, FoundFlag);
12579  if(FoundFlag)
12580  {
12581  TempElement = Track->TrackElementAt(960, ATVecPos);
12582  if(TempElement.SpeedTag > 0)
12583  {
12584  Track->SelectPush(TempElement);
12585  }
12586  }
12587  }
12588  }
12589  // now store inactive elements
12590  for(int x = SelectRect.left; x < SelectRect.right; x++)
12591  {
12592  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12593  {
12594  TTrack::TIMPair IATVecPair = Track->GetVectorPositionsFromInactiveTrackMap(29, x, y, FoundFlag);
12595  if(FoundFlag)
12596  {
12597  TempElement = Track->InactiveTrackElementAt(128, IATVecPair.first);
12598  Track->SelectPush(TempElement); // only want SpeedTag & location set, rest defaults
12599  if(IATVecPair.second != IATVecPair.first) // 2 elements stored at location, i.e. platforms
12600  {
12601  TempElement = Track->InactiveTrackElementAt(129, IATVecPair.second);
12602  Track->SelectPush(TempElement);
12603  }
12604  }
12605  }
12606  }
12607  TrainController->LogEvent("RotLeft-trackstore ok");
12608  //store preferred directions //added at v2.9.0
12609  int PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3;
12610  TPrefDirElement TempPrefDirElement;
12612  for(int x = SelectRect.left; x < SelectRect.right; x++)
12613  {
12614  for(int y = SelectRect.top; y < SelectRect.bottom; y++)
12615  {
12616  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(13, x, y, FoundFlag, PrefDirPos0, PrefDirPos1, PrefDirPos2, PrefDirPos3);
12617  if(FoundFlag)
12618  {
12619  if(PrefDirPos0 > -1)
12620  {
12621  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(10, PrefDirPos0);
12622  SelectPrefDir->ExternalStorePrefDirElement(16, TempPrefDirElement);
12623  }
12624  if(PrefDirPos1 > -1)
12625  {
12626  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(11, PrefDirPos1);
12627  SelectPrefDir->ExternalStorePrefDirElement(17, TempPrefDirElement);
12628  }
12629  if(PrefDirPos2 > -1)
12630  {
12631  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(12, PrefDirPos2);
12632  SelectPrefDir->ExternalStorePrefDirElement(18, TempPrefDirElement);
12633  }
12634  if(PrefDirPos3 > -1)
12635  {
12636  TempPrefDirElement = EveryPrefDir->GetModifiablePrefDirElementAt(13, PrefDirPos3);
12637  SelectPrefDir->ExternalStorePrefDirElement(19, TempPrefDirElement);
12638  }
12639  }
12640  }
12641  }
12642  TrainController->LogEvent("RotLeft-PDstore ok");
12643  // store text items
12644  int LowSelectHPos = SelectRect.left * 16;
12645  int HighSelectHPos = SelectRect.right * 16;
12646  int LowSelectVPos = SelectRect.top * 16;
12647  int HighSelectVPos = SelectRect.bottom * 16;
12648  TextHandler->SelectTextVector.clear();
12649  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
12650  {
12651  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->TextVector.begin(); TextPtr < TextHandler->TextVector.end(); TextPtr++)
12652  {
12653  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos < HighSelectVPos))
12654  {
12655  // have to create a new TextItem in order to create a new Font object
12656  // BUT: only create new items where they don't appear as named location names
12657  // in SelectVector, since those names shouldn't be copied or pasted.
12658  // NB: altered for PasteWithAttributes - at v2.2.0 save the named element but prefix it with "##**"
12659  // so can paste or not depending on which type of paste is being used (unlikely to use that in a real name)
12660  bool SelectVectorNamedElement = false;
12661  AnsiString SelectTextString; // new at v2.2.0
12662  for(unsigned int x = 0; x < Track->SelectVector.size(); x++)
12663  {
12664  if(Track->SelectVector.at(x).LocationName == TextPtr->TextString)
12665  {
12666  SelectVectorNamedElement = true;
12667  break;
12668  }
12669  }
12670  if(SelectVectorNamedElement) // changed at v2.2.0
12671  {
12672  SelectTextString = "##**" + TextPtr->TextString; // new at v2.2.0
12673  }
12674  else // new at v2.2.0
12675  {
12676  SelectTextString = TextPtr->TextString;
12677  }
12678  TTextItem TextItem(TextPtr->HPos, TextPtr->VPos, SelectTextString, TextPtr->Font);
12679  TextHandler->SelectTextVector.push_back(TextItem); // changed at v2.2.0
12680  }
12681  }
12682  }
12683  TrainController->LogEvent("RotLeft-textstore ok");
12684  // store graphic items, but first clear SelectGraphicVector
12685  Track->SelectGraphicVector.clear();
12686  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
12687  {
12688  for(TTrack::TUserGraphicVector::iterator UserGraphicPtr = Track->UserGraphicVector.begin(); UserGraphicPtr < Track->UserGraphicVector.end();
12689  UserGraphicPtr++)
12690  {
12691  if((UserGraphicPtr->HPos >= LowSelectHPos) && ((UserGraphicPtr->HPos + UserGraphicPtr->Width) < HighSelectHPos) && (UserGraphicPtr->VPos >=
12692  LowSelectVPos) && ((UserGraphicPtr->VPos + UserGraphicPtr->Height) < HighSelectVPos))
12693  {
12694  Track->SelectGraphicVector.push_back(*UserGraphicPtr);
12695  }
12696  }
12697  }
12698  TrainController->LogEvent("RotLeft-graphicstore ok");
12699  // now transform the H & V for lh rotate
12700  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
12701  {
12702  int HLoc = SelectRect.left - SelectRect.top + Track->SelectVectorAt(76, x).VLoc;
12703  int VLoc = SelectRect.bottom - 1 + SelectRect.left - Track->SelectVectorAt(77, x).HLoc;
12705  TE.VLoc = VLoc;
12706  TE.HLoc = HLoc;
12707 
12708  TE.ActiveTrackElementName = Track->SelectVectorAt(66, x).ActiveTrackElementName; // these new in v2.4.0 so keeps attributes
12710  TE.Length01 = Track->SelectVectorAt(68, x).Length01;
12711  TE.Length23 = Track->SelectVectorAt(69, x).Length23;
12714  TE.SigAspect = Track->SelectVectorAt(72, x).SigAspect;
12715  Track->SelectVectorAt(73, x) = TE;
12716  }
12717 
12718  int RotLeftLinkArray[10] =
12719  {
12720  0, 7, 4, 1, 8, 5, 2, 9, 6, 3
12721  }; //0 & 5 are never used
12722  TrainController->LogEvent("RotLeft-trackrotate ok");
12723  // now transform the pref dirs
12724  for(unsigned int x = 0; x < SelectPrefDir->PrefDirSize(); x++)
12725  {
12726  int OriginalHLoc = SelectPrefDir->GetFixedPrefDirElementAt(255, x).HLoc; //added at v2.9.1
12727  int OriginalVLoc = SelectPrefDir->GetFixedPrefDirElementAt(256, x).VLoc;
12728  int HLoc = SelectRect.left - SelectRect.top + OriginalVLoc;
12729  int VLoc = SelectRect.bottom - 1 + SelectRect.left - OriginalHLoc;
12730  int ELink = RotLeftLinkArray[SelectPrefDir->GetFixedPrefDirElementAt(257, x).GetELink()];
12731  int XLink = RotLeftLinkArray[SelectPrefDir->GetFixedPrefDirElementAt(258, x).GetXLink()];
12733  //the above line caused errors when Track->GetTrackElementFromAnyTrackMap in RecoverClipboard had Vector passed in by value,
12734  //causing a bad returned value for SpeedTag and other properties. Passing it in by reference fixed it
12735  TPrefDirElement PDE(TE); //this has Link[4] set
12736  PDE.HLoc = HLoc;
12737  PDE.VLoc = VLoc;
12738  PDE.SetELink(ELink);
12739  PDE.SetXLink(XLink);
12740  bool ELinkPosFound = false, XLinkPosFound = false; //these ensure that the link pos is set as low as possible for points
12741  for(int y = 0; y < 4; y++) //changed to y at v2.9.1
12742  {
12743  if(!ELinkPosFound && (PDE.Link[y] == ELink))
12744  {
12745  PDE.SetELinkPos(y);
12746  ELinkPosFound = true;
12747  }
12748  if(!XLinkPosFound && (PDE.Link[y] == XLink))
12749  {
12750  PDE.SetXLinkPos(y);
12751  XLinkPosFound = true;
12752  }
12753  }
12754  //set the CheckCount as before - added at v2.9.1
12755  PDE.SetCheckCount(9); //explicitly set to 9 at v2.9.2
12758  //set the TrackVectorPosition to correspond to the corresponding TrackElement added at v2.9.1
12759  bool FoundFlag = false;
12760  PDE.SetTrackVectorPosition(Track->GetVectorPositionFromTrackMap(65, OriginalHLoc, OriginalVLoc, FoundFlag)); //uses original TV position as TrackMap hasn't changed yet
12761  if(PDE.GetSignedIntTrackVectorPosition() < 0)
12762  {
12763  FoundFlag = false; //probably will be anyway but reset to be sure & test below
12764  }
12766  if(!PDE.EntryExitNumber() || !ELinkPosFound || !XLinkPosFound || !FoundFlag) //error if can't set the number, any link pos not set or !FoundFlag
12767  {
12769  ShowMessage("Unable to re-orientate the preferred directions, these won't be set in the rotated selection");
12770  break;
12771  }
12772  SelectPrefDir->PrefDirVector.at(x) = PDE;
12773  }
12774  TrainController->LogEvent("RotLeft-PDrotate ok");
12775  // reset values in SelectTextVector
12776  for(unsigned int x = 0; x < TextHandler->SelectTextVectorSize(4); x++)
12777  {
12778 // no point trying to locate text properly as it stays horizontal so will always be wrongly placed, just list all itels vertically at lhs
12779 // & if a lot then some will extend beyond the selection
12781  // also subtract half font height for each letter of text, brings position approximately right horizontally
12782  TextItem->HPos = (SelectRect.left) * 16;
12783  TextItem->VPos = (SelectRect.top + x) * 16;
12784  }
12785  TrainController->LogEvent("RotLeft-textrotate ok");
12786  // reset values in SelectGraphicVector
12787  for(unsigned int x = 0; x < Track->SelectGraphicVector.size(); x++)
12788  {
12789  int MidHPosBeforeRotate = Track->SelectGraphicVector.at(x).HPos + Track->SelectGraphicVector.at(x).Width / 2;
12790  int MidVPosBeforeRotate = Track->SelectGraphicVector.at(x).VPos + Track->SelectGraphicVector.at(x).Height / 2;
12791  int MidHPosAfterRotate = ((SelectRect.left - SelectRect.top) * 16) + MidVPosBeforeRotate;
12792  int MidVPosAfterRotate = ((SelectRect.bottom * 16) - 1) + (SelectRect.left * 16) - MidHPosBeforeRotate;
12793  int LeftPosAfterRotate = MidHPosAfterRotate - (Track->SelectGraphicVector.at(x).Width) / 2;
12794  int TopPosAfterRotate = MidVPosAfterRotate - (Track->SelectGraphicVector.at(x).Height) / 2;
12795  Track->SelectGraphicVector.at(x).HPos = LeftPosAfterRotate;
12796  Track->SelectGraphicVector.at(x).VPos = TopPosAfterRotate;
12797  }
12798  Screen->Cursor = TCursor(-2); // Arrow
12800  SetLevel2TrackMode(62);
12801  Utilities->CallLogPop(2124);
12802  }
12803  catch(const Exception &e)
12804  {
12805  ErrorLog(206, e.Message);
12806  }
12807 }
12808 
12809 // ---------------------------------------------------------------------------
12810 
12811 void __fastcall TInterface::PasteMenuItemClick(TObject *Sender)
12812 {
12813  try
12814  {
12815  TrainController->LogEvent("PasteMenuItemClick");
12816  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PasteMenuItemClick");
12817  // Level1Mode = TrackMode;
12818  // SetLevel1Mode(74);
12820  SetLevel2TrackMode(58);
12821  Utilities->CallLogPop(2060);
12822  }
12823  catch(const Exception &e)
12824  {
12825  ErrorLog(198, e.Message);
12826  }
12827 }
12828 
12829 // ---------------------------------------------------------------------------
12830 void __fastcall TInterface::DeleteMenuItemClick(TObject *Sender)
12831 {
12832  try
12833  {
12834  TrainController->LogEvent("DeleteMenuItemClick");
12835  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",DeleteMenuItemClick");
12836  // Level1Mode = TrackMode;
12837  // SetLevel1Mode(75);
12839  SetLevel2TrackMode(38);
12840  Utilities->CallLogPop(1193);
12841  }
12842  catch(const Exception &e)
12843  {
12844  ErrorLog(153, e.Message);
12845  }
12846 }
12847 // ---------------------------------------------------------------------------
12848 
12849 void __fastcall TInterface::SelectLengthsMenuItemClick(TObject *Sender)
12850 {
12851  try
12852  {
12853  TrainController->LogEvent("SelectLengthsMenuItemClick");
12854  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SelectLengthsMenuItemClick");
12855  TrackElementPanel->Visible = false;
12856  TrackLengthPanel->Visible = true;
12857  RestoreAllDefaultLengthsButton->Enabled = true; //added at v2.19.0 as otherwise retained last value and may have been disabled
12858  ResetDefaultLengthButton->Enabled = true; // as above
12859  LengthOKButton->Enabled = true; // as above
12860  TrackLengthPanel->SetFocus();
12861  SelectLengthsFlag = true;
12862  InfoPanel->Visible = true;
12863  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Set values or leave blank for no change";
12865  {
12866  ShowMessage("Note: length value will apply to each element's track within the selection.\n\nThis message will not be shown again.");
12867  LengthWarningSentFlag = true;
12868  }
12869  DistanceBox->Text = "";
12870  SpeedLimitBox->Text = "";
12873 // ResetChangedFileDataAndCaption(, true); //don't need this here after 2.7.0 as included in TrackLengthPanel buttons
12874  Utilities->CallLogPop(1414);
12875  }
12876  catch(const Exception &e)
12877  {
12878  ErrorLog(154, e.Message);
12879  }
12880 }
12881 
12882 // ---------------------------------------------------------------------------
12883 
12884 void __fastcall TInterface::SelectBiDirPrefDirsMenuItemClick(TObject *Sender)
12885 {
12886 /* SelectVector contains all the track elements (and inactive elements but don't need them), so create up to 4 PrefDir
12887  elements from each one, and add each into ConstructPrefDir, then when all added use ConsolidatePrefDirs to add to EveryPrefDir
12888 */
12889  try
12890  {
12891  TrainController->LogEvent("SelectBiDirPrefDirsMenuItemClick");
12892  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SelectBiDirPrefDirsMenuItemClick");
12894  bool FoundFlag = false;
12895  if(Track->SelectVector.empty())
12896  {
12897  Utilities->CallLogPop(1550);
12898  return;
12899  }
12900  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
12901  {
12902  TTrackElement TE = Track->SelectVectorAt(14, x);
12903  int VecPos = Track->GetVectorPositionFromTrackMap(42, TE.HLoc, TE.VLoc, FoundFlag);
12904  if(FoundFlag)
12905  {
12906  if((TE.TrackType == Points) || (TE.TrackType == Bridge) || (TE.TrackType == Crossover)) // 2-track element
12907  {
12908  TPrefDirElement PE0(TE, TE.Link[0], 0, TE.Link[1], 1, VecPos);
12910  TPrefDirElement PE1(TE, TE.Link[1], 1, TE.Link[0], 0, VecPos);
12912  TPrefDirElement PE2(TE, TE.Link[2], 2, TE.Link[3], 3, VecPos);
12914  TPrefDirElement PE3(TE, TE.Link[3], 3, TE.Link[2], 2, VecPos);
12916  }
12917  else if((TE.TrackType == Simple) || (TE.TrackType == Buffers) || (TE.TrackType == SignalPost) || (TE.TrackType == Continuation) ||
12918  (TE.TrackType == GapJump) || (TE.TrackType == FootCrossing))
12919  // need to list these explicitly since inactive elements will still be 'found' if there is an active element
12920  // at the same position
12921  {
12922  TPrefDirElement PE0(TE, TE.Link[0], 0, TE.Link[1], 1, VecPos);
12924  TPrefDirElement PE1(TE, TE.Link[1], 1, TE.Link[0], 0, VecPos);
12926  }
12927  }
12928  }
12930  ResetChangedFileDataAndCaption(22, false);
12931  // RlyFile = false; - don't alter this just for PrefDir changes
12932  Level1Mode = BaseMode; // call this first to clear everything, then set PrefDir mode
12933  SetLevel1Mode(30);
12935  SetLevel1Mode(31); // calls Clearand... to display all PrefDirs
12936  Utilities->CallLogPop(1549);
12937  }
12938  catch(const Exception &e)
12939  {
12940  ErrorLog(155, e.Message);
12941  }
12942 }
12943 
12944 // ---------------------------------------------------------------------------
12945 
12946 void __fastcall TInterface::CancelSelectionMenuItemClick(TObject *Sender)
12947 {
12948  try
12949  {
12950  TrainController->LogEvent("CancelSelectionClick");
12951  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CancelSelectionClick");
12952 // ClearandRebuildRailway(46); //called below
12953  SelectionValid = false;
12954  CancelSelectionFlag = true; // used to avoid RecoverClipboard in pasting when CutMoving selected
12955  Track->CopyFlag = false;
12957  if(Level1Mode == TrackMode)
12958  {
12959  SetLevel1Mode(76); // CancelSelectionFlag needed here
12961  SetLevel2TrackMode(68);
12962  }
12963  else if(Level1Mode == PrefDirMode)
12964  {
12965  SetLevel1Mode(32);
12966  }
12967  CancelSelectionFlag = false; // done with it
12968  ResetSelectRect();
12969  ClearandRebuildRailway(82); // to remove the selection outline
12970  Clipboard()->Clear();
12971  Clipboard()->Close();
12972  Utilities->CallLogPop(1413);
12973  }
12974  catch(const EClipboardException &e) // take no action //non-error catch
12975  {
12976 // Application->MessageBox(L"A clipboard error occurred in the cancel function", L"Message", MB_OK);
12977  TrainController->LogEvent("EClipboardException in CancelSelectionMenuItemClick - message = " + e.Message);
12978  Utilities->CallLogPop(2314);
12979  }
12980  catch(const Exception &e)
12981  {
12982  ErrorLog(156, e.Message);
12983  }
12984 }
12985 
12986 // ---------------------------------------------------------------------------
12987 
12988 void __fastcall TInterface::CheckPrefDirConflictsMenuItemClick(TObject *Sender)
12989 {
12990 //Conflicts consist of track elements without PDs, a preferred direction (PD) that links to a track element without a PD on it, or a PD that links to
12991 //another PD that is set in the wrong direction.
12992 
12993 //function changed to become more comprehensive at v2.13.0
12994  try
12995  {
12996  TrainController->LogEvent("CheckPrefDirConflictsMenuItemClick");
12997  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CheckPrefDirConflictsMenuItemClick");
12998  bool FoundFlag;
12999  int Count = 0;
13000  UnicodeString CountWord = "There are some track elements without preferred directions.\n\n"
13001  "Do you wish to highlight them (YES) or skip this part of the check (NO)?";
13002 
13003  int PD0, PD1, PD2, PD3, HLoc, VLoc, LastHLoc = -2000000, LastVLoc = -2000000; //well outside any conceivable range
13004  AnsiString TempInfo = InfoPanel->Caption;
13005  if(EveryPrefDir->PrefDirSize() <= 0)
13006  {
13007  ShowMessage("No preferred directions set.");
13008  Utilities->CallLogPop(2301);
13009  return;
13010  }
13011  else
13012  {
13013  InfoPanel->Visible = true;
13014  InfoPanel->Caption = "Checking preferred directions - please wait";
13015  InfoPanel->Update();
13016  THVShortPair LasTHVShortPair;
13017  LasTHVShortPair.first = -2000000;
13018  LasTHVShortPair.second = -2000000; //well outside any conceivable range
13019  Screen->Cursor = TCursor(-11); // Hourglass
13020 //check PDs, iterate the map rather than the vector so that pref dirs at the same H & V are consecutive, & can prevent the same element showing more than once
13021 
13022  for(TOnePrefDir::TPrefDir4MultiMapIterator PDMMIt = EveryPrefDir->PrefDir4MultiMap.begin(); PDMMIt != EveryPrefDir->PrefDir4MultiMap.end(); PDMMIt++)
13023  {
13024  bool ELinkFound = false;//, BiDir = false;
13025  int ELink = EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetELink();
13026  int ELinkPos = EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetELinkPos();
13027  int ThisElementHLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).HLoc;
13028  int ThisElementVLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).VLoc;
13029  if((ELink > -1) && (EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[ELinkPos] > -1))
13030  {
13032  Track->TrackElementAt(1024, EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[ELinkPos]).VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13033  if((EveryPrefDir->PrefDirVector.at(PDMMIt->second).TrackType == GapJump) && (ELinkPos == 0)) //0 is the gap position
13034  {
13035  if(PD0 > -1)
13036  {
13037  if(EveryPrefDir->PrefDirVector.at(PD0).TrackType == GapJump) //the corresponding gap
13038  {
13039  if(EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos() == 0) // entry link is at the gap end so it corresponds
13040  {
13041  ELinkFound = true;
13042  }
13043  }
13044  }
13045  if(PD1 > -1)
13046  {
13047  if(EveryPrefDir->PrefDirVector.at(PD1).TrackType == GapJump) //can only be PD0 or PD1 for a gap
13048  {
13049  if(EveryPrefDir->PrefDirVector.at(PD1).GetXLinkPos() == 0) // entry link is at the gap end so it corresponds
13050  {
13051  ELinkFound = true;
13052  }
13053  }
13054  }
13055  }
13056  if(EveryPrefDir->BiDirectionalPrefDir(0, PDMMIt) &&
13057  (Track->TrackElementAt(1454, EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetTrackVectorPosition()).
13058  Config[EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetELinkPos()] == Signal))
13059  {
13060  ELinkFound = true; // ok if this element is a signal at exit end
13061  }
13062  if(BypassPDCrossoverMismatch(0, ThisElementHLoc, ThisElementVLoc))
13063  {
13064  ELinkFound = true; // ok to skip linked points with bidirs on both links and single PDs on other legs
13065  }
13066  if(PD0 > -1)
13067  {
13068  if(EveryPrefDir->PrefDirVector.at(PD0).GetXLink() == (10 - ELink))
13069  {
13070  ELinkFound = true;
13071  }
13072  //ok if a signal at exit end of a single dir PD element for a conflict at the ELink end of a bidirectional PD element (applies for PD0 & 1 only)
13073  else if(EveryPrefDir->BiDirectionalPrefDir(1, PDMMIt) &&
13074  Track->TrackElementAt(1455, EveryPrefDir->PrefDirVector.at(PD0).GetTrackVectorPosition()).
13075  Config[EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos()] == Signal)
13076  {
13077  ELinkFound = true;
13078  }
13079  }
13080  if(PD1 > -1)
13081  {
13082  if(EveryPrefDir->PrefDirVector.at(PD1).GetXLink() == (10 - ELink))
13083  {
13084  ELinkFound = true;
13085  }
13086  //as above ok if a signal at exit end of a single PD element for a conflict at the ELink end of a bidirectional PD element (applies for PD0 & 1 only)
13087  else if(EveryPrefDir->BiDirectionalPrefDir(2, PDMMIt) &&
13088  Track->TrackElementAt(1456, EveryPrefDir->PrefDirVector.at(PD0).GetTrackVectorPosition()).
13089  Config[EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos()] == Signal)
13090  {
13091  ELinkFound = true;
13092  }
13093  }
13094  if(PD2 > -1) //if PD2/3 > -1 then can'#t be a signal
13095  {
13096  if(EveryPrefDir->PrefDirVector.at(PD2).GetXLink() == (10 - ELink))
13097  {
13098  ELinkFound = true;
13099  }
13100  }
13101  if(PD3 > -1)
13102  {
13103  if(EveryPrefDir->PrefDirVector.at(PD3).GetXLink() == (10 - ELink))
13104  {
13105  ELinkFound = true;
13106  }
13107  }
13108  }
13109  if(!ELinkFound && (EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[ELinkPos] > -1))
13110  {
13111  HLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).HLoc;
13112  VLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).VLoc;
13113  if((LastHLoc != HLoc) || (LastVLoc != VLoc))
13114  {
13115  LastHLoc = HLoc;
13116  LastVLoc = VLoc;
13117  while((Display->DisplayOffsetH - HLoc) > 0)
13118  {
13119  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
13120  }
13121  while((HLoc - Display->DisplayOffsetH) > (Utilities->ScreenElementWidth - 1))
13122  {
13124  }
13125  while((Display->DisplayOffsetV - VLoc) > 0)
13126  {
13127  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
13128  }
13129  while((VLoc - Display->DisplayOffsetV) > (Utilities->ScreenElementHeight - 1))
13130  {
13132  }
13134  Display->InvertElement(2, HLoc * 16, VLoc * 16);
13135  Screen->Cursor = TCursor(-2); // Arrow
13136  int Button = Application->MessageBox(L"Preferred direction mismatch at a link between the\n"
13137  "highlighted element and an adjacent element.\n\n"
13138  "The highlighted element may be behind this message\n"
13139  "which can be moved by left clicking the mouse in the\n"
13140  "title bar and dragging it.\n\n"
13141  "The mismatch may or may not matter depending\n"
13142  "on routing requirements during operation.\n\n"
13143  "Click 'OK' to ignore and continue checking or 'Cancel'\n"
13144  "to allow correction.", L"Warning", MB_OKCANCEL | MB_ICONWARNING);
13145  ClearandRebuildRailway(86); // to clear inversion
13146  if(Button == IDCANCEL)
13147  {
13148  InfoPanel->Caption = TempInfo;
13149  Utilities->CallLogPop(2303);
13150  return;
13151  }
13152  Screen->Cursor = TCursor(-11); //Hourglass
13153  Display->Update();
13154  }
13155  }
13156 
13157  bool XLinkFound = false;
13158  int XLink = EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetXLink();
13159  int XLinkPos = EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetXLinkPos();
13160  if((XLink > -1) && (EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[XLinkPos] > -1))
13161  {
13163  Track->TrackElementAt(1026, EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[XLinkPos]).VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13164  if((EveryPrefDir->PrefDirVector.at(PDMMIt->second).TrackType == GapJump) && (XLinkPos == 0)) //0 is the gap position
13165  {
13166  if(PD0 > -1)
13167  {
13168  if(EveryPrefDir->PrefDirVector.at(PD0).TrackType == GapJump) //the corresponding gap
13169  {
13170  if(EveryPrefDir->PrefDirVector.at(PD0).GetELinkPos() == 0) // entry link is at the gap end so it corresponds
13171  {
13172  XLinkFound = true;
13173  }
13174  }
13175  }
13176  if(PD1 > -1)
13177  {
13178  if(EveryPrefDir->PrefDirVector.at(PD1).TrackType == GapJump) //can only be PD0 or PD1 for a gap
13179  {
13180  if(EveryPrefDir->PrefDirVector.at(PD1).GetELinkPos() == 0) // entry link is at the gap end so it corresponds
13181  {
13182  XLinkFound = true;
13183  }
13184  }
13185  }
13186  }
13187  if(EveryPrefDir->BiDirectionalPrefDir(3, PDMMIt) &&
13188  (Track->TrackElementAt(1457, EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetTrackVectorPosition()).
13189  Config[EveryPrefDir->PrefDirVector.at(PDMMIt->second).GetXLinkPos()] == Signal))
13190  {
13191  XLinkFound = true; // ok if this element is a signal at exit end
13192  }
13193  if(BypassPDCrossoverMismatch(1, ThisElementHLoc, ThisElementVLoc))
13194  {
13195  XLinkFound = true; // ok to skip linked points with bidirs on both links and single PDs on other legs
13196  }
13197  if(PD0 > -1)
13198  {
13199  if(EveryPrefDir->PrefDirVector.at(PD0).GetELink() == (10 - XLink))
13200  {
13201  XLinkFound = true;
13202  }
13203  }
13204  if(PD1 > -1)
13205  {
13206  if(EveryPrefDir->PrefDirVector.at(PD1).GetELink() == (10 - XLink))
13207  {
13208  XLinkFound = true;
13209  }
13210  }
13211  if(PD2 > -1)
13212  {
13213  if(EveryPrefDir->PrefDirVector.at(PD2).GetELink() == (10 - XLink))
13214  {
13215  XLinkFound = true;
13216  }
13217  }
13218  if(PD3 > -1)
13219  {
13220  if(EveryPrefDir->PrefDirVector.at(PD3).GetELink() == (10 - XLink))
13221  {
13222  XLinkFound = true;
13223  }
13224  }
13225  }
13226  if(!XLinkFound && (EveryPrefDir->PrefDirVector.at(PDMMIt->second).Conn[XLinkPos] > -1))
13227  {
13228  HLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).HLoc;
13229  VLoc = EveryPrefDir->PrefDirVector.at(PDMMIt->second).VLoc;
13230  if((LastHLoc != HLoc) || (LastVLoc != VLoc))
13231  {
13232  LastHLoc = HLoc;
13233  LastVLoc = VLoc;
13234  while((Display->DisplayOffsetH - HLoc) > 0)
13235  {
13236  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
13237  }
13238  while((HLoc - Display->DisplayOffsetH) > (Utilities->ScreenElementWidth - 1))
13239  {
13241  }
13242  while((Display->DisplayOffsetV - VLoc) > 0)
13243  {
13244  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
13245  }
13246  while((VLoc - Display->DisplayOffsetV) > (Utilities->ScreenElementHeight - 1))
13247  {
13249  }
13251  Display->InvertElement(3, HLoc * 16, VLoc * 16);
13252  Screen->Cursor = TCursor(-2); // Arrow
13253  int Button = Application->MessageBox(L"Preferred direction mismatch at a link between the\n"
13254  "highlighted element and an adjacent element.\n\n"
13255  "The highlighted element may be behind this message\n"
13256  "which can be moved by left clicking the mouse in the\n"
13257  "title bar and dragging it.\n\n"
13258  "The mismatch may or may not matter depending\n"
13259  "on routing requirements during operation.\n\n"
13260  "Click 'OK' to ignore and continue checking or 'Cancel'\n"
13261  "to allow correction.", L"Warning", MB_OKCANCEL | MB_ICONWARNING);
13262  ClearandRebuildRailway(88); // to clear inversion
13263  if(Button == IDCANCEL)
13264  {
13265  InfoPanel->Caption = TempInfo;
13266  Utilities->CallLogPop(2304);
13267  return;
13268  }
13269  Screen->Cursor = TCursor(-11); // Hourglass
13270  Display->Update();
13271  }
13272  }
13273  }
13274 //now check every track element to make sure there's a PD set on it. Get H&V for each, and if single track element ensure PD0 set, else make
13275 //sure both set, but first count them and if > 9 offer to skip
13276  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
13277  {
13278  int HLoc = Track->TrackElementAt(1470, x).HLoc;
13279  int VLoc = Track->TrackElementAt(1471, x).VLoc;
13280  if((Track->TrackElementAt(1472, x).TrackType == Points) || (Track->TrackElementAt(1473, x).TrackType == Crossover) ||
13281  (Track->TrackElementAt(1474, x).TrackType == Bridge))
13282  {
13283  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(34, HLoc, VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13284  //check if either track with no PDs: !Foundflag == none, PD1 == -1 == only one, and last condition covers one having a bidir PD & other none
13285  if(!FoundFlag) //none
13286  {
13287  Count += 2;
13288  }
13289  else if(PD1 == -1) // only one
13290  {
13291  Count++;
13292  }
13293  else if(((PD2 == -1) && (PD1 > -1) && (EveryPrefDir->PrefDirVector.at(PD1).GetELink() == EveryPrefDir->PrefDirVector.at(PD0).GetXLink()) &&
13294  (EveryPrefDir->PrefDirVector.at(PD1).GetXLink() == EveryPrefDir->PrefDirVector.at(PD0).GetELink()))) //only one, other has bidirs
13295  {
13296  Count++;
13297  }
13298  }
13299  else //single track element
13300  {
13301  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(35, HLoc, VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13302  //check if either track with no PDs: !Foundflag == none, PD1 == -1 == only one, and last condition covers one having a bidir PD & other none
13303  if(!FoundFlag)
13304  {
13305  Count++;
13306  }
13307  }
13308  }
13309  if(Count > 20)
13310  {
13311  CountWord = "There are many track elements without preferred directions.\n\n"
13312  "Do you wish to highlight them (YES) or skip this part of the check (NO)?";
13313  }
13314  if(Count > 2)
13315  {
13316  Screen->Cursor = TCursor(-2); // Arrow
13317  int Button = Application->MessageBox(CountWord.c_str(), L"Skip option", MB_YESNO);
13318  ClearandRebuildRailway(96); // to clear inversion
13319  if(Button == IDNO)
13320  {
13321  InfoPanel->Caption = TempInfo;
13322  ShowMessage("Finished");
13323  Utilities->CallLogPop(2484);
13324  return;
13325  }
13326  Screen->Cursor = TCursor(-11); //Hourglass
13327  Display->Update();
13328  }
13329 //continue with the check if haven't returned
13330  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
13331  {
13332  int HLoc = Track->TrackElementAt(1465, x).HLoc;
13333  int VLoc = Track->TrackElementAt(1466, x).VLoc;
13334  if((Track->TrackElementAt(1467, x).TrackType == Points) || (Track->TrackElementAt(1468, x).TrackType == Crossover) ||
13335  (Track->TrackElementAt(1469, x).TrackType == Bridge))
13336  {
13337  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(32, HLoc, VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13338  //check if either track with no PDs: !Foundflag == none, PD1 == -1 == only one, and last condition covers one having a bidir PD & other none
13339  if(!FoundFlag || (PD1 == -1) || ((PD2 == -1) && (PD1 > -1) && (EveryPrefDir->PrefDirVector.at(PD1).GetELink() == EveryPrefDir->PrefDirVector.at(PD0).GetXLink()) &&
13340  (EveryPrefDir->PrefDirVector.at(PD1).GetXLink() == EveryPrefDir->PrefDirVector.at(PD0).GetELink()))) //need to check both as points can meet just one with one PD on each track
13341  {
13342  if((LastHLoc != HLoc) || (LastVLoc != VLoc))
13343  {
13344  LastHLoc = HLoc;
13345  LastVLoc = VLoc;
13346  while((Display->DisplayOffsetH - HLoc) > 0)
13347  {
13348  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
13349  }
13350  while((HLoc - Display->DisplayOffsetH) > (Utilities->ScreenElementWidth - 1))
13351  {
13353  }
13354  while((Display->DisplayOffsetV - VLoc) > 0)
13355  {
13356  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
13357  }
13358  while((VLoc - Display->DisplayOffsetV) > (Utilities->ScreenElementHeight - 1))
13359  {
13361  }
13363  Display->InvertElement(4, HLoc * 16, VLoc * 16);
13364  Screen->Cursor = TCursor(-2); // Arrow
13365  int Button = Application->MessageBox(L"Preferred direction missing at the highlighted element.\n\n"
13366  "The highlighted element may be behind this message\n"
13367  "which can be moved by left clicking the mouse in the\n"
13368  "title bar and dragging it.\n\n"
13369  "The omission may or may not matter depending\n"
13370  "on routing requirements during operation.\n\n"
13371  "Click 'OK' to ignore and continue checking or 'Cancel'\n"
13372  "to allow correction.", L"Warning", MB_OKCANCEL | MB_ICONWARNING);
13373  ClearandRebuildRailway(93); // to clear inversion
13374  if(Button == IDCANCEL)
13375  {
13376  InfoPanel->Caption = TempInfo;
13377  Utilities->CallLogPop(2482);
13378  return;
13379  }
13380  Screen->Cursor = TCursor(-11); //Hourglass
13381  Display->Update();
13382  }
13383  }
13384  }
13385  else //single track element
13386  {
13387  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(33, HLoc, VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13388  //check if either track with no PDs: !Foundflag == none, PD1 == -1 == only one, and last condition covers one having a bidir PD & other none
13389  if(!FoundFlag)
13390  {
13391  if((LastHLoc != HLoc) || (LastVLoc != VLoc))
13392  {
13393  LastHLoc = HLoc;
13394  LastVLoc = VLoc;
13395  while((Display->DisplayOffsetH - HLoc) > 0)
13396  {
13397  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
13398  }
13399  while((HLoc - Display->DisplayOffsetH) > (Utilities->ScreenElementWidth - 1))
13400  {
13402  }
13403  while((Display->DisplayOffsetV - VLoc) > 0)
13404  {
13405  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
13406  }
13407  while((VLoc - Display->DisplayOffsetV) > (Utilities->ScreenElementHeight - 1))
13408  {
13410  }
13412  Display->InvertElement(5, HLoc * 16, VLoc * 16);
13413  Screen->Cursor = TCursor(-2); // Arrow
13414  int Button = Application->MessageBox(L"Preferred direction missing at the highlighted element.\n\n"
13415  "The highlighted element may be behind this message\n"
13416  "which can be moved by left clicking the mouse in the\n"
13417  "title bar and dragging it.\n\n"
13418  "The omission may or may not matter depending\n"
13419  "on routing requirements during operation.\n\n"
13420  "Click 'OK' to ignore and continue checking or 'Cancel'\n"
13421  "to allow correction.", L"Warning", MB_OKCANCEL | MB_ICONWARNING);
13422  ClearandRebuildRailway(95); // to clear inversion
13423  if(Button == IDCANCEL)
13424  {
13425  InfoPanel->Caption = TempInfo;
13426  Utilities->CallLogPop(2483);
13427  return;
13428  }
13429  Screen->Cursor = TCursor(-11); //Hourglass
13430  Display->Update();
13431  }
13432  }
13433  }
13434  }
13435  }
13436  Screen->Cursor = TCursor(-2); // Arrow
13437  ShowMessage("Finished");
13438  InfoPanel->Caption = TempInfo;
13439  Utilities->CallLogPop(2305);
13440  }
13441  catch(const Exception &e) //non-error catch
13442  {
13443  Screen->Cursor = TCursor(-2); // Arrow
13444  ShowMessage("Error in preferred direction checking, unable to complete the check");
13445  Utilities->CallLogPop(2306);
13446  }
13447 }
13448 
13449 //---------------------------------------------------------------------------
13450 
13451 bool TInterface::BypassPDCrossoverMismatch(int Caller, int HLoc, int VLoc)
13452 /* If there's a crossover between two lines that each have single PDs set on them and the crossover has bidirectioal PDs then ordinarily a mismatch would be flagged
13453 for each crossover. However since no PD route can be set over the crossover whatever PDs are set on them there's no point in flagging the mismatch.
13454 This function checks if the element passed in is a point with 3 PDs set (PD2 > -1 & PD3 == -1), i.e. only one leg has a bidir PD, and that this bidir PD leg is connected
13455 to another point bidir leg with 3 PDs set. If so it returns true, else false.
13456 */
13457 {
13458  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BypassPDCrossoverMismatch " + AnsiString(HLoc) + "," + AnsiString(VLoc));
13459  int PD0 = -1, PD1 = -1, PD2 = -1, PD3 = -1;
13460  int PDLinked0 = -1, PDLinked1 = -1, PDLinked2 = -1, PDLinked3 = -1;
13461  int BidirLinkPos = -1;
13462  bool FoundFlag = false, BidirOnLink = false;
13463  EveryPrefDir->GetVectorPositionsFromPrefDir4MultiMap(19, HLoc, VLoc, FoundFlag, PD0, PD1, PD2, PD3);
13464  if((PD0 == -1) || (PD2 == -1) || (PD3 > -1))
13465  {
13466  Utilities->CallLogPop(2457);
13467  return false;
13468  }
13469  if(PD0 > -1)
13470  {
13471  if(EveryPrefDir->PrefDirVector.at(PD0).TrackType != Points)
13472  {
13473  Utilities->CallLogPop(2458);
13474  return false;
13475  }
13476  }
13477 //here it's points with one bidir leg
13478 //now find the link [1] or [3] that corresponds to the bidir leg and check it's another point with the same characteristics
13479  if((EveryPrefDir->PrefDirVector.at(PD0).GetXLink() == EveryPrefDir->PrefDirVector.at(PD1).GetELink()) &&
13480  (EveryPrefDir->PrefDirVector.at(PD1).GetXLink() == EveryPrefDir->PrefDirVector.at(PD0).GetELink()))
13481  { // bidir leg is PD0 & PD1
13482  if(EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos() == 1)
13483  {
13484  BidirLinkPos = 1;
13485  }
13486  else if(EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos() == 3)
13487  {
13488  BidirLinkPos = 3;
13489  }
13490  else if(EveryPrefDir->PrefDirVector.at(PD0).GetELinkPos() == 1)
13491  {
13492  BidirLinkPos = 1;
13493  }
13494  else if(EveryPrefDir->PrefDirVector.at(PD0).GetELinkPos() == 3)
13495  {
13496  BidirLinkPos = 3;
13497  }
13498  }
13499  else if((EveryPrefDir->PrefDirVector.at(PD0).GetXLink() == EveryPrefDir->PrefDirVector.at(PD2).GetELink()) &&
13500  (EveryPrefDir->PrefDirVector.at(PD2).GetXLink() == EveryPrefDir->PrefDirVector.at(PD0).GetELink()))
13501  { // bidir leg is PD0 & PD2
13502  if(EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos() == 1)
13503  {
13504  BidirLinkPos = 1;
13505  }
13506  else if(EveryPrefDir->PrefDirVector.at(PD0).GetXLinkPos() == 3)
13507  {
13508  BidirLinkPos = 3;
13509  }
13510  else if(EveryPrefDir->PrefDirVector.at(PD0).GetELinkPos() == 1)
13511  {
13512  BidirLinkPos = 1;
13513  }
13514  else if(EveryPrefDir->PrefDirVector.at(PD0).GetELinkPos() == 3)
13515  {
13516  BidirLinkPos = 3;
13517  }
13518  }
13519  else if((EveryPrefDir->PrefDirVector.at(PD2).GetXLink() == EveryPrefDir->PrefDirVector.at(PD1).GetELink()) &&
13520  (EveryPrefDir->PrefDirVector.at(PD1).GetXLink() == EveryPrefDir->PrefDirVector.at(PD2).GetELink()))
13521  { // bidir leg is PD1 & PD2
13522  if(EveryPrefDir->PrefDirVector.at(PD1).GetXLinkPos() == 1)
13523  {
13524  BidirLinkPos = 1;
13525  }
13526  else if(EveryPrefDir->PrefDirVector.at(PD1).GetXLinkPos() == 3)
13527  {
13528  BidirLinkPos = 3;
13529  }
13530  else if(EveryPrefDir->PrefDirVector.at(PD1).GetELinkPos() == 1)
13531  {
13532  BidirLinkPos = 1;
13533  }
13534  else if(EveryPrefDir->PrefDirVector.at(PD1).GetELinkPos() == 3)
13535  {
13536  BidirLinkPos = 3;
13537  }
13538  }
13539  if((BidirLinkPos == -1) || (BidirLinkPos > 3))//shouldn't be
13540  {
13541  Utilities->CallLogPop(2459);
13542  return false;
13543  }
13544 //now find the linked element and check it's a point with 3 PDs set & link bidir
13545  int TVPos = Track->TrackElementAt(1458, EveryPrefDir->PrefDirVector.at(PD0).GetTrackVectorPosition()).Conn[BidirLinkPos];
13546  int TVLinkPos = Track->TrackElementAt(1459, EveryPrefDir->PrefDirVector.at(PD0).GetTrackVectorPosition()).ConnLinkPos[BidirLinkPos];
13547  if(TVPos == -1)
13548  {
13549  Utilities->CallLogPop(2460);
13550  return false;
13551  }
13552  if((TVLinkPos == 0) || (TVLinkPos == 2) || (TVLinkPos > 3))
13553  {
13554  Utilities->CallLogPop(2461);
13555  return false;
13556  }
13557  if(Track->TrackElementAt(1460, TVPos).TrackType != Points)
13558  {
13559  Utilities->CallLogPop(2462);
13560  return false;
13561  }
13562  if((TVLinkPos != 1) && (TVLinkPos != 3)) //must be linked at trailing leg
13563  {
13564  Utilities->CallLogPop(2463);
13565  return false;
13566  }
13568  Track->TrackElementAt(1461, TVPos).VLoc, FoundFlag, PDLinked0, PDLinked1, PDLinked2, PDLinked3);
13569  if(!FoundFlag)
13570  {
13571  Utilities->CallLogPop(2464);
13572  return false;
13573  }
13574  if((PDLinked0 == -1) || (PDLinked2 == -1) || (PDLinked3 > -1)) //must have exactly 3 PDs
13575  {
13576  Utilities->CallLogPop(2465);
13577  return false;
13578  }
13579 //now need bidir PDs on linked leg
13580  if((EveryPrefDir->PrefDirVector.at(PDLinked0).GetXLink() == EveryPrefDir->PrefDirVector.at(PDLinked1).GetELink()) &&
13581  (EveryPrefDir->PrefDirVector.at(PDLinked0).GetELink() == EveryPrefDir->PrefDirVector.at(PDLinked1).GetXLink()))
13582  { //bidir leg is PDLinked0 & PDLinked1
13583  if(EveryPrefDir->PrefDirVector.at(PDLinked0).GetXLinkPos() == TVLinkPos)
13584  {
13585  BidirOnLink = true;
13586  }
13587  if(EveryPrefDir->PrefDirVector.at(PDLinked0).GetELinkPos() == TVLinkPos)
13588  {
13589  BidirOnLink = true;
13590  }
13591 
13592  }
13593  else if((EveryPrefDir->PrefDirVector.at(PDLinked0).GetXLink() == EveryPrefDir->PrefDirVector.at(PDLinked2).GetELink()) &&
13594  (EveryPrefDir->PrefDirVector.at(PDLinked0).GetELink() == EveryPrefDir->PrefDirVector.at(PDLinked2).GetXLink()))
13595  { //bidir leg is PDLinked0 & PDLinked2
13596  if(EveryPrefDir->PrefDirVector.at(PDLinked0).GetXLinkPos() == TVLinkPos)
13597  {
13598  BidirOnLink = true;
13599  }
13600  if(EveryPrefDir->PrefDirVector.at(PDLinked0).GetELinkPos() == TVLinkPos)
13601  {
13602  BidirOnLink = true;
13603  }
13604 
13605  }
13606  else if((EveryPrefDir->PrefDirVector.at(PDLinked1).GetXLink() == EveryPrefDir->PrefDirVector.at(PDLinked2).GetELink()) &&
13607  (EveryPrefDir->PrefDirVector.at(PDLinked1).GetELink() == EveryPrefDir->PrefDirVector.at(PDLinked2).GetXLink()))
13608  { //bidir leg is PDLinked0 & PDLinked2
13609  if(EveryPrefDir->PrefDirVector.at(PDLinked1).GetXLinkPos() == TVLinkPos)
13610  {
13611  BidirOnLink = true;
13612  }
13613  if(EveryPrefDir->PrefDirVector.at(PDLinked1).GetELinkPos() == TVLinkPos)
13614  {
13615  BidirOnLink = true;
13616  }
13617  }
13618  if(!BidirOnLink)
13619  {
13620  Utilities->CallLogPop(2466);
13621  return false;
13622  }
13623  Utilities->CallLogPop(2467);
13624  return true;
13625 }
13626 
13627 //---------------------------------------------------------------------------
13628 
13629 void __fastcall TInterface::LoadTimetableMenuItemClick(TObject *Sender)
13630 {
13631  try
13632  {
13633  TrainController->LogEvent("LoadTimetableMenuItemClick");
13634  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LoadTimetableMenuItemClick");
13635  TimetableDialog->Filter = "Timetable file (*.ttb)|*.ttb";
13636  // reset all message flags, stops them being given twice new at v2.4.0
13637  TrainController->SSHigh = false;
13638 // TrainController->MRSHigh = false; removed at v2.21.0
13639 // TrainController->MRSLow = false;
13640  TrainController->MassHigh = false;
13641  TrainController->BFHigh = false;
13642  TrainController->BFLow = false;
13643  TrainController->PwrHigh = false;
13644  TrainController->SigSHigh = false;
13645  TrainController->SigSLow = false;
13646  if(TimetableDialog->Execute())
13647  {
13648  if(TimetableDialog->InitialDir != TPath::GetDirectoryName(TimetableDialog->FileName)) // new at v2.6.0 to retain a new directory
13649  {
13650  TimetableDialog->InitialDir = TPath::GetDirectoryName(TimetableDialog->FileName);
13651  SaveTTDialog->InitialDir = TPath::GetDirectoryName(TimetableDialog->FileName);
13652  }
13653  TrainController->LogEvent("LoadTimetable " + TimetableDialog->FileName);
13654  bool CheckLocationsExistInRailwayTrue = true;
13655  if(TrainController->TimetableIntegrityCheck(0, AnsiString(TimetableDialog->FileName).c_str(), true, CheckLocationsExistInRailwayTrue))
13656  // true for GiveMessages
13657  {
13658  Screen->Cursor = TCursor(-11); // Hourglass;
13659  std::ifstream TTBLFile(AnsiString(TimetableDialog->FileName).c_str(), std::ios_base::binary);
13660  if(TTBLFile.is_open())
13661  {
13662  bool SessionFileFalse = false;
13663  if(BuildTrainDataVectorForLoadFile(0, TTBLFile, true, CheckLocationsExistInRailwayTrue, SessionFileFalse)) // true for GiveMessages
13664  {
13665  SaveTempTimetableFile(0, TimetableDialog->FileName);
13666  } // don't need an 'else' as messages given in BuildTrainDataVectorForLoadFile
13667 
13668  }
13669  else
13670  {
13671  ShowMessage("Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
13672  }
13673  Screen->Cursor = TCursor(-2); // Arrow
13674  } // if(TimetableIntegrityCheck
13675  else
13676  {
13677  ShowMessage("Timetable integrity check failed - unable to load " + TimetableDialog->FileName + ". \n\nPlease make sure that you are using the latest program version and that the timetable contains no errors.");
13678  //message clarified at v2.14.0
13679  }
13680  } // if(TimetableDialog->Execute())
13681 
13682  // else ShowMessage("Load Aborted");
13683  Utilities->CallLogPop(752);
13684  }
13685  catch(const Exception &e) //made a non-error catch at v2.14.0 following Albie Vowles error of 15/12/22
13686  {
13687  TrainController->StopTTClockMessage(137, "Timetable file failed to load - is the latest program version in use?\nIf so then the file may be corrupt.\n\nError message: " + e.Message);
13688  Screen->Cursor = TCursor(-2); // Arrow;
13689  Utilities->CallLogPop(2553);
13690 // ErrorLog(34, e.Message);
13691  }
13692 }
13693 
13694 // ---------------------------------------------------------------------------
13695 void __fastcall TInterface::TakeSignallerControlMenuItemClick(TObject *Sender)
13696 {
13697  try
13698  {
13699  TrainController->LogEvent("SignallerControl1Click");
13700  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SignallerControl1Click");
13702  Train.SignallerStoppingFlag = false;
13703  Train.TrainMode = Signaller;
13704  if(Train.MaxRunningSpeed > Train.SignallerMaxSpeed)
13705  {
13706  Train.MaxRunningSpeed = Train.SignallerMaxSpeed;
13707  }
13708  if(Train.Stopped())
13709  {
13710  Train.SignallerStopped = true; // condition added at v2.4.0 to allow for taking sig control of failed moving trains
13711  }
13712  Train.CallingOnFlag = false; // in case was set, wouldn't start anyway if called on as SignallerStopped = true
13714  Train.PlotTrain(5, Display);
13715  AnsiString LocName = "";
13716  if(Train.LeadElement > -1)
13717  {
13718  LocName = Track->TrackElementAt(633, Train.LeadElement).ActiveTrackElementName;
13719  }
13720  if((LocName == "") && (Train.MidElement > -1))
13721  {
13722  LocName = Track->TrackElementAt(634, Train.MidElement).ActiveTrackElementName;
13723  }
13724  // store the value that allow restoration of tt control or not - RestoreTimetableLocation
13725  if(Train.RevisedStoppedAtLoc() && (LocName != ""))
13726  {
13727  Train.RestoreTimetableLocation = LocName;
13728  }
13729  else
13730  {
13731  Train.RestoreTimetableLocation = "";
13732  }
13733  // check whether need to offer 'pass stop signal'
13734  if(!Train.StoppedAtSignal && Train.RevisedStoppedAtLoc())
13735  {
13736  int NextElementPosition = Track->TrackElementAt(775, Train.LeadElement).Conn[Train.LeadExitPos];
13737  int NextEntryPos = Track->TrackElementAt(776, Train.LeadElement).ConnLinkPos[Train.LeadExitPos];
13738  if((NextElementPosition > -1) && (NextEntryPos > -1))
13739  {
13740  if((Track->TrackElementAt(777, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
13741  (Track->TrackElementAt(778, NextElementPosition).Attribute == 0))
13742  {
13743  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
13744  // forwards, but don't change the background colour so still shows as stopped at location
13745  Train.StoppedAtSignal = true;
13746  }
13747  }
13748  }
13749  // find element ID if no locname
13750  if((LocName == "") && Train.LeadElement > -1)
13751  {
13752  LocName = Track->TrackElementAt(635, Train.LeadElement).ElementID;
13753  }
13754  if((LocName == "") && (Train.MidElement > -1))
13755  {
13756  LocName = Track->TrackElementAt(636, Train.MidElement).ElementID;
13757  }
13758  Train.LogAction(0, Train.HeadCode, "", TakeSignallerControl, LocName, "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
13759  Utilities->CallLogPop(1772);
13760  }
13761  catch(const Exception &e)
13762  {
13763  ErrorLog(157, e.Message);
13764  }
13765 }
13766 
13767 // ---------------------------------------------------------------------------
13768 
13769 void __fastcall TInterface::TimetableControlMenuItemClick(TObject *Sender) //Restore timetable control
13770 {
13771  try
13772  {
13773  TrainController->LogEvent("TimetableControlMenuItemClick");
13774  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TimetableControlMenuItemClick");
13776  Train.SignallerStoppingFlag = false;
13777  Train.TrainMode = Timetable;
13778  Train.SignallerStopped = false;
13779  Train.StoppedAfterSPAD = false;
13780  Train.SPADFlag = false;
13783 // red headcode[0]
13784  Train.PlotTrain(6, Display);
13785  AnsiString LocName = "", LeadElementLocName = "", MidElementLocName = "", RequiredLocName = Train.ActionVectorEntryPtr->LocationName;
13786  if(Train.LeadElement > -1) //this naming procedure changed at v2.9.1 as a train might have one element at a station and the other at a non-station named location
13787  //but only one is the location in the timetable, and that is the RequiredLocName. Discovered for LS41 at Lincoln.
13788  {
13789  LeadElementLocName = Track->TrackElementAt(645, Train.LeadElement).ActiveTrackElementName;
13790  }
13791  if(Train.MidElement > -1)
13792  {
13793  MidElementLocName = Track->TrackElementAt(647, Train.MidElement).ActiveTrackElementName;
13794  }
13795  if((LeadElementLocName == "") && (MidElementLocName == "") && (Train.LeadElement > -1))
13796  {
13797  LeadElementLocName = Track->TrackElementAt(646, Train.LeadElement).ElementID;
13798  }
13799  if((MidElementLocName == "") && (Train.MidElement > -1)) //changed from LeadElementLocName at v2.12.0 - must have been a mistake
13800  {
13801  MidElementLocName = Track->TrackElementAt(648, Train.MidElement).ElementID;
13802  }
13803  if((LeadElementLocName == RequiredLocName) || (MidElementLocName == RequiredLocName))
13804  {
13805  LocName = RequiredLocName;
13806  }
13807  else if(LeadElementLocName != "") //these two else ifs added at v2.12.0 as noticed that if a train is restored to tt control at
13808  { //a loc that isn't RequiredLocName then LocName is ""
13809  LocName = LeadElementLocName; //LeadElement takes precedence if both named
13810  }
13811  else if(MidElementLocName != "")
13812  {
13813  LocName = MidElementLocName;
13814  }
13816  {//Train.TreatPassAsTimeLocDeparture added at v2.12.0 so background stays pale green after taking signaller control, as LocationType is EnRoute for a Pass
13817  if(Train.TreatPassAsTimeLocDeparture) //this if... else... segment added at v2.12.0 to ensure one or the other true but not both
13818  {
13819  Train.StoppedAtLocation = false;
13820  }
13821  else
13822  {
13823  Train.StoppedAtLocation = true;
13824  }
13825  Train.StoppedAtSignal = false;
13826 // added at v2.7.0 as if had been stopped at signal before tt control restored then background colour would change to normal when signal cleared even when not due to depart
13827  Train.LastActionTime = TrainController->TTClockTime; // by itself this only affects trains that have still to arrive, if waiting to
13828  // depart the departure time & TRS time have already been calculated so need to
13829  // force a recalculation - see below
13830 // Train.DepartureTimeSet = false; // force it to be recalculated based on new LastActionTime (if waiting to arrive this is false anyway)
13831 //can't use the above after random delays introduced, as lose the delay when restore tt control (see FinsburyPark [discord name] error noted below)
13832  if(!Train.TrainFailed)
13833  {
13835  } // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
13836  else
13837  {
13838  Train.PlotTrainWithNewBackgroundColour(56, clTrainFailedBackground, Display); //added at v2.18.0 to restore failed background
13839  }
13840 
13841  if((Train.ActionVectorEntryPtr->FormatType == TimeLoc) && (Train.ActionVectorEntryPtr->ArrivalTime >= TDateTime(0)))
13842  {
13843  // Timetable indicates that train still waiting to arrive for a TimeLoc arrival so send message and mark as arrived
13844  Train.LogAction(28, Train.HeadCode, "", Arrive, LocName, "", Train.ActionVectorEntryPtr->ArrivalTime, Train.ActionVectorEntryPtr->Warning);
13845  Train.ActualArrivalTime = TrainController->TTClockTime; //added at v2.23.0
13846  Train.ActionVectorEntryPtr++; // advance pointer past arrival //added at v1.2.0
13847  }
13848  else if((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) && !(Train.TimeTimeLocArrived))
13849  {
13850  // Timetable indicates that train still waiting to arrive for a TimeTimeLoc arrival so send message and mark as arrived
13851  Train.LogAction(29, Train.HeadCode, "", Arrive, LocName, "", Train.ActionVectorEntryPtr->ArrivalTime, Train.ActionVectorEntryPtr->Warning);
13852  Train.TimeTimeLocArrived = true;
13853  Train.ActualArrivalTime = TrainController->TTClockTime; //added at v2.23.0
13854  // NB: No need for 'Train.ActionVectorEntryPtr++' because still to act on the departure time
13855  }
13856 
13858  if((AVE->DepartureTime >= TDateTime(0)) && ((AVE->FormatType == TimeLoc) || (AVE->FormatType == TimeTimeLoc)))
13859  {
13860  if((Train.ReleaseTime - Train.LastActionTime) < TDateTime(30.0 / 86400)) //due to release in less than 30 seconds, added at v2.13.0 to correct
13861  { //FinsburyPark (discord name) error reported 29/05/22
13862  Train.ReleaseTime = Train.LastActionTime + TDateTime(30.0 / 86400);
13863  }
13864  if((Train.ReleaseTime - Train.ActualArrivalTime) < TDateTime((AVE->MinDwellTime - 0.05) / 86400)) //added at v2.23.0
13865  {
13866  Train.ReleaseTime = Train.ActualArrivalTime + TDateTime(AVE->MinDwellTime / 86400);
13867  }
13868  Train.TRSTime = Train.ReleaseTime - TDateTime(10.0/86400); //added at v2.14.0 as this needs to be reset too to plot correct train b'gnd colour
13869  }
13870  }
13871  else
13872  {
13873  int NextElementPos = -1; // addition for v1.3.2 due to Carwyn Thomas' error
13874  int NextEntryPos = -1; // ---ditto---
13875  if(Train.LeadElement > -1) // ---ditto---
13876  {
13877  // ---ditto---
13878  NextElementPos = Track->TrackElementAt(658, Train.LeadElement).Conn[Train.LeadExitPos]; // had 'int' prefix before additions
13879  NextEntryPos = Track->TrackElementAt(659, Train.LeadElement).ConnLinkPos[Train.LeadExitPos]; // ---ditto---
13880  } // ---ditto---
13881 
13882  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
13883  if(!Train.TrainFailed)
13884  {
13885  Train.PlotTrainWithNewBackgroundColour(31, clNormalBackground, Display); // to remove other background if was present, moved from
13886  } // within Train.AbleToMove at v2.4.0 to cancel signal stop background
13887  else
13888  {
13889  Train.PlotTrainWithNewBackgroundColour(57, clTrainFailedBackground, Display); //added at v2.18.0 to restore failed background
13890  }
13891 
13892  if(Train.AbleToMove(1)) // if has no power
13893  {
13894  Train.EntrySpeed = 0; // moved from below for v1.3.2 after Carwyn Thomas error
13895  Train.EntryTime = TrainController->TTClockTime; // ---Ditto---
13896  Train.FirstHalfMove = true; // ---Ditto---
13897  if((NextElementPos > -1) && (NextEntryPos > -1)) // changed from if(NextElementPos >= 0) as above
13898  {
13899  // Train.EntrySpeed = 0;
13900  // Train.EntryTime = TrainController->TTClockTime;
13901  // Train.FirstHalfMove = true;
13902  Train.SetTrainMovementValues(15, NextElementPos, NextEntryPos);
13903  }
13904  // else follow the continuations //added these 3 conditions for v1.3.2 after Carwyn Thomas error
13905  else if((Train.LeadElement > -1) && (Track->TrackElementAt(894, Train.LeadElement).TrackType == Continuation))
13906  {
13907  Train.SetTrainMovementValues(21, Train.LeadElement, Train.LeadEntryPos); // Use LeadElement for calcs if lead is a continuation
13908  }
13909  else if((Train.MidElement > -1) && (Track->TrackElementAt(895, Train.MidElement).TrackType == Continuation))
13910  {
13911  Train.SetTrainMovementValues(22, Train.MidElement, Train.MidEntryPos); // Use MidElement for calcs if Mid is a continuation
13912  }
13913  else if((Train.LagElement > -1) && (Track->TrackElementAt(896, Train.LagElement).TrackType == Continuation))
13914  {
13915  Train.SetTrainMovementValues(23, Train.LagElement, Train.LagEntryPos); // Use LagElement for calcs if Lag is a continuation
13916  }
13917  }
13918  else if(Train.StoppedAtSignal)
13919  {
13920  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
13921  if(!Train.TrainFailed)
13922  {
13924  }
13925  // TrainController->LogActionError(42, Train.HeadCode, "", SignalHold, Track->TrackElementAt(757, NextElementPos).ElementID);
13926  }
13927  }
13928  Train.LogAction(1, Train.HeadCode, "", RestoreTimetableControl, LocName, "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
13929  Utilities->CallLogPop(1195);
13930  }
13931  catch(const Exception &e)
13932  {
13933  ErrorLog(158, e.Message);
13934  }
13935 }
13936 
13937 // ---------------------------------------------------------------------------
13938 
13939 void __fastcall TInterface::ChangeDirectionMenuItemClick(TObject *Sender)
13940 {
13941  try
13942  {
13943  TrainController->LogEvent("ChangeDirectionMenuItemClick");
13944  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ChangeDirectionMenuItemClick");
13946  Train.SignallerStoppingFlag = false;
13947  Train.SignallerChangeTrainDirection(0); // this unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
13948  Train.SignallerStopped = true;
13949  AnsiString LocName = "";
13950  if(Train.LeadElement > -1)
13951  {
13952  LocName = Track->TrackElementAt(637, Train.LeadElement).ActiveTrackElementName;
13953  }
13954  if((LocName == "") && (Train.MidElement > -1))
13955  {
13956  LocName = Track->TrackElementAt(638, Train.MidElement).ActiveTrackElementName;
13957  }
13958  if((LocName == "") && Train.LeadElement > -1)
13959  {
13960  LocName = Track->TrackElementAt(639, Train.LeadElement).ElementID;
13961  }
13962  if((LocName == "") && (Train.MidElement > -1))
13963  {
13964  LocName = Track->TrackElementAt(640, Train.MidElement).ElementID;
13965  }
13966  Train.LogAction(2, Train.HeadCode, "", SignallerChangeDirection, LocName, "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
13967  Utilities->CallLogPop(1196);
13968  }
13969  catch(const Exception &e)
13970  {
13971  ErrorLog(159, e.Message);
13972  }
13973 }
13974 // ---------------------------------------------------------------------------
13975 
13976 void __fastcall TInterface::MoveForwardsMenuItemClick(TObject *Sender)
13977 {
13978  try
13979  {
13980  TrainController->LogEvent("MoveForwardsMenuItemClick");
13981  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MoveForwardsMenuItemClick");
13983  if(Train.StoppedAtLocation && (Train.ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && !TrainLeaveWarningSent) //added at v2.14.0
13984  {
13985  UnicodeString MessageStr = "Please be aware that a train moved from a location prior to its departure time must be returned to that location "
13986  "in order to restore timetable control\n\nThis message will not be shown again\n\nOK to continue, Cancel to abort";
13987  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
13989  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_OKCANCEL);
13990  TrainLeaveWarningSent = true;
13991  TrainController->BaseTime = TDateTime::CurrentDateTime();
13993  if(button == IDCANCEL)
13994  {
13995  Utilities->CallLogPop(2557);
13996  return;
13997  }
13998  }
13999  Train.SignallerStoppingFlag = false;
14000  if(!Train.AbleToMove(2))
14001  {
14002  // shouldn't be here as when unable to move MoveForwards shouldn't be enabled, but leave in as a precaution
14003  Utilities->CallLogPop(1197);
14004  return;
14005  }
14006  Train.SignallerStopped = false;
14007  Train.StoppedAfterSPAD = false; // in case had been set
14008  Train.SPADFlag = false;
14009  Train.StoppedAtLocation = false; // may not have been set but reset anyway
14010  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
14012  Train.EntrySpeed = 0;
14014  Train.FirstHalfMove = true;
14015  int NextElementPos = -1; // addition for v1.3.2 due to Carwyn Thomas error
14016  int NextEntryPos = -1; // ---ditto---
14017  if(Train.LeadElement > -1) // ---ditto---
14018  {
14019  // ---ditto---
14020  NextElementPos = Track->TrackElementAt(652, Train.LeadElement).Conn[Train.LeadExitPos]; // had 'int' prefix before additions
14021  NextEntryPos = Track->TrackElementAt(657, Train.LeadElement).ConnLinkPos[Train.LeadExitPos]; // ---ditto---
14022  } // ---ditto---
14023 
14024  if((NextElementPos > -1) && (NextEntryPos > -1))
14025  {
14026  Train.SetTrainMovementValues(14, NextElementPos, NextEntryPos); // NextElement is the element to be entered
14027  }
14028  // else follow the continuations
14029  else if((Train.LeadElement > -1) && (Track->TrackElementAt(784, Train.LeadElement).TrackType == Continuation))
14030  {
14031  Train.SetTrainMovementValues(17, Train.LeadElement, Train.LeadEntryPos); // Use LeadElement for calcs if lead is a continuation
14032  }
14033  else if((Train.MidElement > -1) && (Track->TrackElementAt(785, Train.MidElement).TrackType == Continuation))
14034  {
14035  Train.SetTrainMovementValues(18, Train.MidElement, Train.MidEntryPos); // Use MidElement for calcs if Mid is a continuation
14036  }
14037  else if((Train.LagElement > -1) && (Track->TrackElementAt(786, Train.LagElement).TrackType == Continuation))
14038  {
14039  Train.SetTrainMovementValues(19, Train.LagElement, Train.LagEntryPos); // Use LagElement for calcs if Lag is a continuation
14040  }
14041  Train.LogAction(3, Train.HeadCode, "", SignallerMoveForwards, "", "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
14042  Utilities->CallLogPop(1198);
14043  }
14044  catch(const Exception &e)
14045  {
14046  ErrorLog(160, e.Message);
14047  }
14048 }
14049 // ---------------------------------------------------------------------------
14050 
14051 void __fastcall TInterface::SignallerJoinedByMenuItemClick(TObject *Sender)
14052 {
14053  // new at v2.4.0
14054  try
14055  {
14056  TrainController->LogEvent("JoinedByMenuItemClick");
14057  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",JoinedByMenuItemClick");
14058  TTrain *TrainToBeJoinedBy;
14060  if(ThisTrain.IsThereAnAdjacentTrain(1, TrainToBeJoinedBy)) // this must come before both powers zero check in order to set a valid TrainToBeJoinedBy
14061  {
14062  if(TrainToBeJoinedBy->TrainMode != Signaller)
14063  {
14064  TrainController->StopTTClockMessage(91, "Adjacent train must be under signaller control in order to join");
14065  Utilities->CallLogPop(2156);
14066  return;
14067  }
14068  // here if there is an adjacent train under signaller control
14069 
14070 /* restriction removed at v2.19.0
14071  if((TrainToBeJoinedBy->PowerAtRail < 1) && (ThisTrain.PowerAtRail < 1))
14072  {
14073  ShowMessage("Can't join two trains when both are without power");
14074  Utilities->CallLogPop(2157);
14075  return;
14076  }
14077 */
14078  AnsiString UnableToJoinIfWaitingToJoinMessage = "Can't join two trains that are waiting to join under\n"
14079  "timetable control. Manoeuvre them both to the join\n"
14080  "location, make sure they are adjacent, and restore\n"
14081  "timetable control. They will then join at the timetabled\n"
14082  "time, or if that has passed then after 30 seconds.";
14083  //add condition for Micke's error reported 19/05/22 by email (add as 2 conditions for simplicity)
14084  if((TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo") &&
14086  {
14087  ShowMessage(UnableToJoinIfWaitingToJoinMessage);
14088  Utilities->CallLogPop(2536);
14089  return;
14090  }
14091  if((TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "jbo") &&
14093  {
14094  ShowMessage(UnableToJoinIfWaitingToJoinMessage);
14095  Utilities->CallLogPop(2537);
14096  return;
14097  }
14098  AnsiString TrainToBeJoinedByHeadCode = TrainToBeJoinedBy->HeadCode;
14099  // set new values for mass etc
14100  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
14101  double OwnBrakeForce = ThisTrain.MaxBrakeRate * ThisTrain.Mass;
14102  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + ThisTrain.Mass);
14103 
14104  // set new values for mass etc
14105  if(ThisTrain.MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed) //this added at v2.11.1 as had been omitted before
14106  {
14107  ThisTrain.MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
14108  }
14109  ThisTrain.Mass += TrainToBeJoinedBy->Mass;
14110  ThisTrain.MaxBrakeRate = CombinedBrakeRate;
14111  ThisTrain.PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
14112  ThisTrain.AValue = sqrt(2 * ThisTrain.PowerAtRail / ThisTrain.Mass);
14113 
14114  TrainToBeJoinedBy->TrainGone = true; // this will cause other train to be deleted
14115  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
14116  AnsiString LocName = "";
14117  if(ThisTrain.LeadElement > -1)
14118  {
14119  LocName = Track->TrackElementAt(979, ThisTrain.LeadElement).ActiveTrackElementName;
14120  }
14121  if((LocName == "") && (ThisTrain.MidElement > -1))
14122  {
14123  LocName = Track->TrackElementAt(980, ThisTrain.MidElement).ActiveTrackElementName;
14124  }
14125  if((LocName == "") && ThisTrain.LeadElement > -1)
14126  {
14127  LocName = Track->TrackElementAt(981, ThisTrain.LeadElement).ElementID;
14128  }
14129  if((LocName == "") && (ThisTrain.MidElement > -1))
14130  {
14131  LocName = Track->TrackElementAt(982, ThisTrain.MidElement).ElementID;
14132  }
14133  ThisTrain.StoppedWithoutPower = true;
14134  if(ThisTrain.PowerAtRail >= 1)
14135  {
14136  ThisTrain.StoppedWithoutPower = false;
14137  }
14138  ThisTrain.TrainFailed = false; // if had failed then no longer failed, even if joining train has no power
14139  if(!ThisTrain.RevisedStoppedAtLoc())
14140  {
14141  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
14143  }
14144  else
14145  {
14146  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
14148  }
14149  ThisTrain.SignallerStopped = true; // maybe as well as stopped without power, though that takes precedence in floating window
14150  ThisTrain.LogAction(34, ThisTrain.HeadCode, TrainToBeJoinedBy->HeadCode, SignallerJoin, LocName, "", TDateTime(0), false); // TDateTime isn't used
14151  ThisTrain.ZeroPowerNoFrontSplitMessage = false; // added at v2.4.0, no need to include TrainToBeJoinedBy as that will be removed
14152  ThisTrain.ZeroPowerNoRearSplitMessage = false;
14153  ThisTrain.FailedTrainNoFinishJoinMessage = false;
14154  ThisTrain.ZeroPowerNoJoinedByMessage = false;
14155  ThisTrain.ZeroPowerNoCDTMessage = false;
14156  ThisTrain.ZeroPowerNoNewServiceMessage = false;
14158  ThisTrain.ZeroPowerNoRepeatShuttleMessage = false;
14160  Utilities->CallLogPop(2158);
14161  }
14162  }
14163  catch(const Exception &e)
14164  {
14165  ErrorLog(207, e.Message);
14166  }
14167 }
14168 // ---------------------------------------------------------------------------
14169 
14170 void __fastcall TInterface::RepairFailedTrainMenuItemClick(TObject *Sender)
14171 {
14172  // added at v2.4.0
14173  try
14174  {
14175  TrainController->LogEvent("RepairFailedTrainMenuItemClick");
14176  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RepairFailedTrainMenuItemClick");
14178  Train.TrainFailed = false;
14179  Train.StoppedWithoutPower = false;
14180  Train.SignallerStopped = true;
14181  if(!Train.RevisedStoppedAtLoc())
14182  {
14183  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
14185  }
14186  else
14187  {
14188  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
14190  }
14191  Train.PowerAtRail = Train.OriginalPowerAtRail; // recover from original value, new at v2.4.0
14192  Train.AValue = sqrt(2 * Train.PowerAtRail / Train.Mass);
14193  Train.SetTrainMovementValues(24, Train.LeadElement, Train.LeadEntryPos);
14194  AnsiString LocName = "";
14195  if(Train.LeadElement > -1)
14196  {
14197  LocName = Track->TrackElementAt(983, Train.LeadElement).ActiveTrackElementName;
14198  }
14199  if((LocName == "") && (Train.MidElement > -1))
14200  {
14201  LocName = Track->TrackElementAt(984, Train.MidElement).ActiveTrackElementName;
14202  }
14203  if((LocName == "") && Train.LeadElement > -1)
14204  {
14205  LocName = Track->TrackElementAt(985, Train.LeadElement).ElementID;
14206  }
14207  if((LocName == "") && (Train.MidElement > -1))
14208  {
14209  LocName = Track->TrackElementAt(986, Train.MidElement).ElementID;
14210  }
14211  Train.LogAction(35, Train.HeadCode, "", RepairFailedTrain, LocName, "", TrainController->TTClockTime, false); // false for no warning
14212  Train.ZeroPowerNoFrontSplitMessage = false;
14213  Train.ZeroPowerNoRearSplitMessage = false;
14214  Train.FailedTrainNoFinishJoinMessage = false;
14215  Train.ZeroPowerNoJoinedByMessage = false;
14216  Train.ZeroPowerNoCDTMessage = false;
14217  Train.ZeroPowerNoNewServiceMessage = false;
14219  Train.ZeroPowerNoRepeatShuttleMessage = false;
14221  Utilities->CallLogPop(2159);
14222  }
14223  catch(const Exception &e)
14224  {
14225  ErrorLog(208, e.Message);
14226  }
14227 }
14228 
14229 // ---------------------------------------------------------------------------
14230 
14231 void __fastcall TInterface::SkipTimetabledActionsMenuItemClick(TObject *Sender)
14232 { //note that the TTClock stays stopped when SkipTTActionsListBox is visible - sets WarningHover to true in ClockTimer2
14233 /* Only enable this for stopped at signal or stopped at location.
14234 
14235 If stopped at signal then next action will be TimeLoc arrival, TimeTimeLoc, Pass or Fer.
14236 
14237 If stopped at a location then next action will be TimeTimeLoc dep/TimeLoc dep/jbo/fsp/rsp/cdt/dsc/cms/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs.
14238 
14239 FormatType: NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle,
14240 SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime, ExitRailway
14241 
14242 SequenceType: NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry
14243 */
14244  try
14245  {
14246  TrainController->LogEvent("SkipTimetabledActionsMenuItemClick");
14247  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SkipTimetabledActionsMenuItemClick");
14248  TTrain Train = TrainController->TrainVectorAtIdent(55, SelectedTrainID); //Train not modified here so don't need reference
14249  SkipTTActionsListBox->Clear();
14250  SkipTTActionsListBox->ExtendedSelect = false; //this and the next allow only one item to be selected
14251  SkipTTActionsListBox->MultiSelect = false;
14252  ReminderListBox->Visible = false; //make invisible in case weren't
14253  ReminderHeaderPanel->Visible = false;
14254 
14255  //populate the listbox
14256  AnsiString TTStr = Train.FloatingTimetableString(2, Train.ActionVectorEntryPtr);
14257  AnsiString OneLine;
14258  int Count = 0;
14259  int NewLinePos = TTStr.Pos('\n');
14260  SkipTTActionsListBox->Width = 400;
14261  SkipListHeaderPanel->Width = 400;
14262  for(int x = 0; x < 30; x++)
14263  {
14264  if((TTStr.Length() > 1) && (NewLinePos <= TTStr.Length()) && (NewLinePos != 0)) //i.e. all lines apart from the last where there is no newline
14265  {
14266  OneLine = TTStr.SubString(1, NewLinePos); //includes the newline
14267  if(OneLine == "\n")
14268  {
14269  break; //break before Count increment
14270  }
14271  if(OneLine == "Timetable:\n") //update TTStr & Newline but don't increment Count //added at v2.13.0 as 'Timetable:' added
14272  {
14273  TTStr = TTStr.SubString(NewLinePos + 1, TTStr.Length() - NewLinePos); //removed -1 after NewLinePos at v2.19.0, was an error
14274  NewLinePos = TTStr.Pos('\n');
14275  continue;
14276  }
14277  Count++;
14278  SkipTTActionsListBox->Items->Add(OneLine);
14279  TTStr = TTStr.SubString(NewLinePos + 1, TTStr.Length() - NewLinePos); //removed -1 after NewLinePos at v2.19.0, was an error
14280  NewLinePos = TTStr.Pos('\n');
14281  }
14282  else if((TTStr.Length() > 1) && (NewLinePos == 0) && ((TTStr.SubString(3, 1) == ':') || (TTStr.SubString(1, 5) == "Termi"))) //last line
14283  {
14284  OneLine = TTStr;
14285  Count++;
14286  SkipTTActionsListBox->Items->Add(OneLine);
14287  break;
14288  }
14289  if(TTStr.Length() <2)
14290  {
14291  break;
14292  }
14293  AnsiString EndStr = OneLine.SubString(8, 5);
14294  //need these last checks in case last floating line is an allowable exit or a new service departure time which aren't wanted in the skip list
14295  if((EndStr == "Form ") || (EndStr == "Join ") || (EndStr == "Exit ")) //all these are preceded by a time & start at character 8
14296  {
14297  break;
14298  }
14299  }
14300  if(Count == 0)
14301  {
14302  ShowMessage("No timetabled events");
14303  Utilities->CallLogPop(2428);
14304  return;
14305  }
14306  SkipTTActionsListBox->Height = (SkipTTActionsListBox->ItemHeight * Count) + 4;
14307 //position listbox
14308  int Left = RightClickTrainMousePosX + MainScreen->Left + 16; // so lhs of window is WindowOffset to the right of the mouse pos
14309  if((Left + SkipTTActionsListBox->Width) > MainScreen->Left + MainScreen->Width)
14310  {
14311  Left = RightClickTrainMousePosX - SkipTTActionsListBox->Width + 16;
14312  }
14313  int Top = RightClickTrainMousePosY + MainScreen->Top + 16; // so top of window is one element below the mouse pos (ScreenY + MainScreen->Top would be at mouse pos)
14314  if((Top + SkipTTActionsListBox->Height) > MainScreen->Top + MainScreen->Height)
14315  {
14316  Top = RightClickTrainMousePosY - SkipTTActionsListBox->Height + 79; // so bottom of window is one element above the mouse pos (95 would be at mouse pos)
14317  if(Top < 30)
14318  {
14319  Top = 30;
14320  }
14321  }
14322  SkipTTActionsListBox->Left = Left; // new at v2.7.0 in place of above
14323  SkipListHeaderPanel->Left = Left;
14324  SkipTTActionsListBox->Top = Top;
14325  SkipListHeaderPanel->Top = Top - 34; //this panel has a height of 34
14327  Utilities->CallLogPop(2418);
14328  }
14329  catch(const Exception &e)
14330  {
14331  ErrorLog(241, e.Message);
14332  }
14333 }
14334 
14335 //---------------------------------------------------------------------------
14336 
14337 void __fastcall TInterface::SetReminderMenuItemClick(TObject *Sender) //added at v2.19.0
14338 { //note that the TTClock stays stopped when ReminderListBox is visible - sets WarningHover to true in ClockTimer2
14339  try
14340  {
14341  TrainController->LogEvent("SetReminderMenuItemClick");
14342  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SetReminderMenuItemClick");
14343  TTrain Train = TrainController->TrainVectorAtIdent(70, SelectedTrainID); //Train not modified here so don't need reference
14344  ReminderListBox->Clear();
14345  ReminderListBox->ExtendedSelect = false; //this and the next allow only one item to be selected
14346  ReminderListBox->MultiSelect = false;
14347  SkipTTActionsListBox->Visible = false; //make invisible in case weren't
14348  SkipListHeaderPanel->Visible = false;
14349  //populate the listbox
14350  AnsiString TTStr = Train.FloatingTimetableString(3, Train.ActionVectorEntryPtr);
14351  AnsiString OneLine;
14352  int Count = 0;
14353  int NewLinePos = TTStr.Pos('\n');
14354  ReminderListBox->Width = 200;
14355  ReminderHeaderPanel->Width = 200;
14356  for(int x = 0; x < 30; x++)
14357  {
14358  if((TTStr.Length() > 1) && (NewLinePos <= TTStr.Length()) && (NewLinePos != 0)) //i.e. all lines apart from the last where there is no newline
14359  {
14360  OneLine = TTStr.SubString(1, NewLinePos); //includes the newline
14361  if(OneLine == "\n")
14362  {
14363  break; //break before Count increment
14364  }
14365  if(OneLine == "Timetable:\n") //update TTStr & Newline but don't increment Count //added at v2.13.0 as 'Timetable:' added
14366  {
14367  TTStr = TTStr.SubString(NewLinePos + 1, TTStr.Length() - NewLinePos);
14368  NewLinePos = TTStr.Pos('\n');
14369  continue;
14370  }
14371  Count++;
14372  ReminderListBox->Items->Add(OneLine);
14373  TTStr = TTStr.SubString(NewLinePos + 1, TTStr.Length() - NewLinePos);
14374  NewLinePos = TTStr.Pos('\n');
14375  }
14376  else if((TTStr.Length() > 1) && (NewLinePos == 0) && ((TTStr.SubString(3, 1) == ':') || (TTStr.SubString(1, 5) == "Termi"))) //last line
14377  {
14378  OneLine = TTStr;
14379  Count++;
14380  ReminderListBox->Items->Add(OneLine);
14381  break;
14382  }
14383  if(TTStr.Length() <2)
14384  {
14385  break;
14386  }
14387  AnsiString EndStr = OneLine.SubString(8, 5);
14388  //need these last checks in case last floating line is an allowable exit or a new service departure time which aren't wanted in the list
14389  if((EndStr == "Form ") || (EndStr == "Join ") || (EndStr == "Exit ")) //all these are preceded by a time & start at character 8
14390  {
14391  break;
14392  }
14393  }
14394  if(Count == 0)
14395  {
14396  ShowMessage("No timetabled events");
14397  Utilities->CallLogPop(2693);
14398  return;
14399  }
14400  ReminderListBox->Height = (ReminderListBox->ItemHeight * Count) + 4;
14401 //position listbox
14402  int Left = RightClickTrainMousePosX + MainScreen->Left + 16; // so lhs of window is WindowOffset to the right of the mouse pos
14403  if((Left + ReminderListBox->Width) > MainScreen->Left + MainScreen->Width)
14404  {
14405  Left = RightClickTrainMousePosX - ReminderListBox->Width + 16;
14406  }
14407  int Top = RightClickTrainMousePosY + MainScreen->Top + 16; // so top of window is one element below the mouse pos (ScreenY + MainScreen->Top would be at mouse pos)
14408  if((Top + ReminderListBox->Height) > MainScreen->Top + MainScreen->Height)
14409  {
14410  Top = RightClickTrainMousePosY - ReminderListBox->Height + 79; // so bottom of window is one element above the mouse pos (95 would be at mouse pos)
14411  if(Top < 30)
14412  {
14413  Top = 30;
14414  }
14415  }
14416  ReminderListBox->Left = Left; // new at v2.7.0 in place of above
14417  ReminderHeaderPanel->Left = Left;
14418  ReminderListBox->Top = Top;
14419  ReminderHeaderPanel->Top = Top - 34; //this panel has a height of 34
14421  Utilities->CallLogPop(2694);
14422  }
14423  catch(const Exception &e)
14424  {
14425  ErrorLog(248, e.Message);
14426  }
14427 
14428 }
14429 
14430 //---------------------------------------------------------------------------
14431 
14432 void __fastcall TInterface::SkipTTActionsListBoxMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
14433 /*
14434 If stopped at signal then allowable next action will be TimeLoc arrival, TimeTimeLoc, Pass or Fer.
14435 
14436 If stopped at a location then save the depart time then allowable next action will be TimeLoc arrival, TimeTimeLoc, Pass or Fer.
14437 
14438 FormatType: NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle,
14439 SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime, ExitRailway
14440 
14441 SequenceType: NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry
14442 */
14443 {
14444  try
14445  {
14446  if(Button == mbRight) //added at v2.23.0 to prevent a crash as ItemIndex is -1 (existing fault)
14447  {
14448  return;
14449  }
14450  TrainController->LogEvent("SkipTTActionsListBoxMouseUp, " + AnsiString(X) + ',' + AnsiString(Y));
14451  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SkipTTActionsListBoxMouseUp");
14452  TTrain &Train = TrainController->TrainVectorAtIdent(56, SelectedTrainID); //Train.ActionVectorPtr advance here so need reference
14453  Train.SkipPtrValue = 0;
14454  if(SkipTTActionsListBox->Items->Text != "") //not empty
14455  {
14456  Train.SelSkipString = SkipTTActionsListBox->Items->Strings[SkipTTActionsListBox->ItemIndex]; //index starts at 0
14457  }
14458  if(Train.StoppedAtSignal)
14459  {
14460  //Calc cumulative dwell times that are skipped
14461  int Count = 0, PassNum = 0, SkippedEvents = 0;
14462  if(SkipTTActionsListBox->ItemIndex == 0)
14463  {
14464  ShowMessage("This is already the next event, nothing will be skipped");
14465  Utilities->CallLogPop(2436);
14466  return;
14467  }
14468  for(TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr; Count < SkipTTActionsListBox->ItemIndex; AVEPtr++)
14469  {
14470  if((AVEPtr->FormatType == TimeTimeLoc) && (AVEPtr->ArrivalTime != AVEPtr->DepartureTime))
14471  {//arr & dep in a single AVEntry if different arr & dep times but two listings, if have same arr & dep time then only a single listing,
14472  Count += 2;
14473  }
14474  else
14475  {
14476  Count++;
14477  }
14478  PassNum++;
14479  if((AVEPtr->Command == "cdt") || (AVEPtr->Command == "dsc") || (AVEPtr->Command == "cms") || (AVEPtr->Command == "pas") || ((AVEPtr->FormatType == TimeLoc) && (AVEPtr->DepartureTime != TDateTime(-1))))
14480  //don't count cdts, dscs, cmss, passes or departures as missed events (note that can't be a finish)
14481  {
14482  continue;
14483  }
14484  else
14485  {
14486  SkippedEvents++;
14487  }
14488  }
14489  AnsiString StartStr = Train.SelSkipString.SubString(8, 4);
14490  if((StartStr != "Arri") && (StartStr != "Pass") && (StartStr != "Exit"))
14491  {
14492  ShowMessage("When stopped at a signal the selected next event must be 'Arrive...', 'Pass...', or 'Exit railway...'");
14493  Utilities->CallLogPop(2429);
14494  return;
14495  }
14496  //advance the pointer but ask for confirmation first
14497  int button = Application->MessageBox(L"This will skip all events before the selection,\n\nOK to proceed?", L"", MB_YESNO);
14498  if(button == IDYES)
14499  {
14500  AnsiString SkipTTLBString = AnsiString(SkipTTActionsListBox->Items->Strings[SkipTTActionsListBox->ItemIndex]); //added at v2.12.0 as doubt over newline in ListBox strings
14501  if(SkipTTLBString[SkipTTLBString.Length()] == '\n') //strip the newline if there is one as one is added after PerfStr sent to PerformanceFile
14502  {
14503  SkipTTLBString = SkipTTLBString.SubString(1, (SkipTTLBString.Length() - 1));
14504  }
14505  Train.ActionVectorEntryPtr += PassNum;
14506  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " " + AnsiString(SkippedEvents)
14507  + " significant events skipped until " + SkipTTLBString;
14508  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
14509  Utilities->PerformanceFile.flush(); //added at v2.13.0
14510  TrainController->SkippedTTEvents += SkippedEvents;
14511  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
14512 
14513  }
14515  }
14516  else if(Train.RevisedStoppedAtLoc())
14517  {
14518  //Calc cumulative dwell times that are skipped
14519  int Count = 0, PassNum = 0, SkippedEvents = 0;
14520  TActionVectorEntry DepEntry;
14521  Train.SkippedDeparture = false;
14522  if(SkipTTActionsListBox->ItemIndex == 0)
14523  {
14524  ShowMessage("This is already the next event, nothing will be skipped");
14525  Utilities->CallLogPop(2437);
14526  return;
14527  }
14528  if(((Train.ActionVectorEntryPtr->FormatType == TimeLoc) && (Train.ActionVectorEntryPtr->DepartureTime != TDateTime(-1))) ||
14529  (Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc)) //departure next
14530  {
14531  if(SkipTTActionsListBox->ItemIndex == 1) //need above condition as the next action might not be a departure
14532  {
14533  ShowMessage("This is already the next event after the departure, nothing will be skipped");
14534  Utilities->CallLogPop(2703);
14535  return;
14536  }
14537  }
14538  for(TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr; Count < SkipTTActionsListBox->ItemIndex; AVEPtr++) //Count < rather than == because incremented at end
14539  {
14540  if(AVEPtr->DepartureTime > TDateTime(0)) //departure action
14541  {
14542  if(AVEPtr->LocationName == Train.ActionVectorEntryPtr->LocationName)
14543  {
14544  Train.SkippedDeparture = true;
14545  }
14546  }
14547  if((AVEPtr->FormatType == TimeTimeLoc) && (AVEPtr->ArrivalTime != AVEPtr->DepartureTime) && (AVEPtr != Train.ActionVectorEntryPtr))
14548  {//arr & dep in a single AVEntry if different arr & dep times but two listings, if have same arr & dep time then only a single listing,
14549  //if first entry is a TimeTimeLoc departure then only one listing
14550  Count += 2;
14551  }
14552  else
14553  {
14554  Count++;
14555  }
14556  PassNum++;
14557  if((AVEPtr->Command == "cdt") || (AVEPtr->Command == "dsc") || (AVEPtr->Command == "cms") || (AVEPtr->Command == "pas") ||
14558  ((AVEPtr->FormatType == TimeLoc) && (AVEPtr->DepartureTime > TDateTime(0))) ||
14559  (Train.SkippedDeparture && (AVEPtr == Train.ActionVectorEntryPtr)))
14560  //don't count cdts, dscs, cmss, passes or departures as missed events (note that can't be a finish), or first departure if SkippedDeparture is true
14561  {
14562  continue;
14563  }
14564  else
14565  {
14566  SkippedEvents++;
14567  }
14568  }
14569  TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr + PassNum; //set to the selected action so can check if at same location
14570  Train.TrainSkippedEvents = SkippedEvents;
14571  AnsiString StartStr = Train.SelSkipString.SubString(8, 4);
14572  if(Train.SkippedDeparture && (AVEPtr->LocationName == Train.ActionVectorEntryPtr->LocationName) && (AVEPtr->ArrivalTime == TDateTime(-1)) && (AVEPtr->FormatType != PassTime))
14573  { //if selected action is at same location and SkippedDeparture is true (i.e returned to it after leaving), then keep SkippedDep provided that the action is arrive or pass
14574  Train.SkippedDeparture = false;
14575  }
14576  if((StartStr != "Arri") && (StartStr != "Pass") && (StartStr != "Exit") && (AVEPtr->LocationName != Train.ActionVectorEntryPtr->LocationName))
14577  {
14578  ShowMessage("When stopped at a location the selected next event must either occur at the same location or be 'Arrive...', 'Pass...', or 'Exit railway...'");
14579  Train.SkippedDeparture = false;
14580  Train.SkipPtrValue = 0;
14581  Utilities->CallLogPop(2435);
14582  return;
14583  }
14584  if(Train.SkippedDeparture)
14585  {
14586  Train.SkipPtrValue = AVEPtr - &(Train.TrainDataEntryPtr->ActionVector.at(0)); //i.e. ActionVectorEntryPtr value above start
14587  } //can't save pointer itself as will be stored in a session file
14588  //advance the pointer but ask for confirmation first
14589  UnicodeString Message = "This will skip all events before the selection.\n\nOK to proceed?";
14590  if(Train.SkippedDeparture)
14591  {
14592  Message = "This will skip all events between the departure and the selection.\n\n"
14593  "Note that no more events may be skipped for this train until after\n"
14594  "it departs from the current location\n\nOK to proceed?";
14595  }
14596  int button = Application->MessageBox(Message.c_str(), L"", MB_YESNO);
14597  if(button == IDYES)
14598  {
14599  AnsiString SkipTTLBString = AnsiString(SkipTTActionsListBox->Items->Strings[SkipTTActionsListBox->ItemIndex]); //added at v2.12.0 as doubt over newline in
14600  //ListBox strings, mainly there is one but seemingly not always
14601  if(SkipTTLBString[SkipTTLBString.Length()] == '\n') //strip the newline if there is one as one is added after PerfStr sent to PerformanceFile
14602  {
14603  SkipTTLBString = SkipTTLBString.SubString(1, (SkipTTLBString.Length() - 1));
14604  }
14605  if(!Train.SkippedDeparture)
14606  {
14607  Train.ActionVectorEntryPtr += PassNum; //points to the next action, if a dep is skipped then the pointer is incremented later
14608  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " " +
14609  AnsiString(Train.TrainSkippedEvents) + " significant events skipped until " + SkipTTLBString;
14611  Train.TrainSkippedEvents = 0;
14612  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
14613  Utilities->PerformanceFile.flush(); //added at v2.13.0
14614  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
14615  }
14616  else
14617  {
14618  Train.ActionsSkippedFlag = true; //set to prevent any further skips until after the departure
14619  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " " +
14620  AnsiString(Train.TrainSkippedEvents) + " significant events skipped after the departure until " + SkipTTLBString;
14621  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
14622  Utilities->PerformanceFile.flush(); //added at v2.13.0
14623  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
14624  }
14625  }
14626  else
14627  {
14628  Train.SkippedDeparture = false;
14629  Train.SkipPtrValue = 0;
14630  Train.ActionsSkippedFlag = false;
14631  }
14633  }
14634  Utilities->CallLogPop(2427);
14635  }
14636  catch(const Exception &e)
14637  {
14638  ErrorLog(242, e.Message);
14639  }
14640 }
14641 
14642 //---------------------------------------------------------------------------
14643 
14644 void __fastcall TInterface::ReminderListBoxMouseUp(TObject *Sender, TMouseButton Button,
14645  TShiftState Shift, int X, int Y) //added at v2.19.0
14646 {
14647  try
14648  {
14649  TrainController->LogEvent("ReminderListBoxMouseUp, " + AnsiString(X) + ',' + AnsiString(Y));
14650  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ReminderListBoxMouseUp");
14651  TTrain &Train = TrainController->TrainVectorAtIdent(71, SelectedTrainID); //Train.ActionVectorPtr not advanced here But keep for consistency
14652  if(ReminderListBox->Items->Text != "") //not empty
14653  {
14654  Train.SelReminderString = ReminderListBox->Items->Strings[ReminderListBox->ItemIndex]; //ItemIndex is the index of the selected item
14655  } //index starts at 0
14656  int Count = 0, AVPtrIncrement = 0;
14657  AnsiString ReminderString = AnsiString(ReminderListBox->Items->Strings[ReminderListBox->ItemIndex]);
14658  int PosArDep = ReminderString.Pos("Arrive & depart");
14659  int PosArr = ReminderString.Pos("Arrive at");
14660  int PosDep = ReminderString.Pos("Depart from");
14661 
14662  for(TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr; Count < ReminderListBox->ItemIndex; AVEPtr++) //stop when reach selected iten
14663  {
14664  AnsiString DepTimeFromAVEPtr = Utilities->Format96HHMM(Train.GetTrainTime(73, AVEPtr->DepartureTime));
14665  AnsiString DepTimeFromListBox = ReminderString.SubString(1, 5);
14666 
14667  if((AVEPtr->FormatType == TimeTimeLoc) && (AVEPtr->ArrivalTime != AVEPtr->DepartureTime))
14668  {//arr & dep in a single AVEntry if different arr & dep times but two listings, if have same arr & dep time then only a single listing,
14669  Count += 2; //Count counts the increment of items in the listbox
14670  }
14671  else
14672  {
14673  Count++;
14674  }
14675  int PosLoc = ReminderString.Pos(AVEPtr->LocationName);
14676 
14677  if((AVEPtr->FormatType != TimeTimeLoc) || (PosDep == 0) || (PosLoc == 0) || (DepTimeFromAVEPtr != DepTimeFromListBox))
14678  {//TimeTimeLocs have two entries in the list box, one for arrive and one for depart, unless the arrival and departure times are the same,
14679  //and if select such an entry reminders are given for both arrival and departure. For others,
14680  //don't want to increment AVPtrIncrement (counts the increment of items in the ActionVector)
14681  //if have selected a departure, because it's at the same pointer as the arrival. So only increment AVPtrIncrement if ActionVectorEntryPtr
14682  //doesn't correspond to selected item, i.e. is not a TimeTimeLoc, not a departure, not at selected location, or has a different different
14683  //departure time
14684  AVPtrIncrement++;
14685  }
14686  }
14687  //set the reminder but ask for confirmation first
14688  if(ReminderString[ReminderString.Length()] == '\n') //strip the newline if there is one as one is added after PerfStr sent to PerformanceFile
14689  {
14690  ReminderString = ReminderString.SubString(1, (ReminderString.Length() - 1));
14691  }
14692  int PosFjo = ReminderString.Pos("Join ");
14693  if(PosFjo > 0)
14694  {
14695  ShowMessage("Reminders cannot be given for 'Join XXXX' events (where XXXX is the joining service). For a train join warning set a reminder "
14696  "for the joining train at its 'Joined by XXXX' event");
14698  Utilities->CallLogPop(2695);
14699  return;
14700  }
14701  if((Train.ActionVectorEntryPtr + AVPtrIncrement)->Warning)
14702  {
14703  ShowMessage("There is already a warning at this event, there is no need for a reminder where there is already a warning");
14705  Utilities->CallLogPop(2696);
14706  return;
14707  }
14708  UnicodeString Msg;
14709  if(PosArDep > 0)
14710  {
14711  Msg = L"This will set a reminder at '" + ReminderString + L"'\n(notifications given for arrival only).\n\nOK to proceed?";
14712  } //above message changed at v2.20.1, earlier said would give notifications for both arr & dep, but doesn't
14713  else
14714  {
14715  Msg = L"This will set a reminder at '" + ReminderString + L"'.\n\nOK to proceed?";
14716  }
14717  int button = Application->MessageBox(Msg.c_str(), L"", MB_YESNO);
14718  if(button == IDYES)
14719  {
14720 //AnsiString ActionStr = ""; //diagnostics
14722 
14723 //AnsiString DepTimeFromAVPtr = Utilities->Format96HHMM(Train.GetTrainTime(74, (AVPtr + AVPtrIncrement)->DepartureTime)); //diagnostics
14724 //AnsiString DepTimeFromLB = ReminderString.SubString(1, 5); //diagnostics
14725 
14726  if(PosArDep > 0)
14727  {
14728  (AVPtr + AVPtrIncrement)->Reminder = 4; //both arr & dep
14729 //ActionStr = "Both"; //diagnostics
14730  }
14731  else if(PosArr > 0)
14732  {
14733 //ActionStr = "Arr"; //diagnostics
14734  if((AVPtr + AVPtrIncrement)->Reminder == 2) //dep already set on its own so set both
14735  {
14736  (AVPtr + AVPtrIncrement)->Reminder = 4; //arr + dep
14737 //ActionStr = "Both"; //diagnostics
14738  }
14739  else
14740  {
14741  (AVPtr + AVPtrIncrement)->Reminder = 3; //arr only
14742  }
14743  }
14744  else if(PosDep > 0)
14745  {
14746 //ActionStr = "Dep"; //diagnostics
14747  if((AVPtr + AVPtrIncrement)->Reminder == 3) //arr already set on its own so set both
14748  {
14749  (AVPtr + AVPtrIncrement)->Reminder = 4; //arr + dep
14750 //ActionStr = "Both"; //diagnostics
14751  }
14752  else
14753  {
14754  (AVPtr + AVPtrIncrement)->Reminder = 2; //dep only
14755  }
14756  }
14757  else
14758  {
14759  (AVPtr + AVPtrIncrement)->Reminder = 1; //not arr, dep or both
14760  }
14761  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " Reminder set at event '" +
14762  ReminderString + "'.";
14763  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
14764  Utilities->PerformanceFile.flush(); //added at v2.13.0
14765  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
14766 
14767 //double ArrTime = double((AVPtr + AVPtrIncrement)->ArrivalTime); //diagnostics
14768 //double DepTime = double((AVPtr + AVPtrIncrement)->DepartureTime); //diagnostics
14769 //double EvTime = double((AVPtr + AVPtrIncrement)->EventTime); //diagnostics
14770 
14771 //ShowMessage("Rem=" + AnsiString((AVPtr + AVPtrIncrement)->Reminder) + "\nAction=" + ActionStr + "\nAVPtrInc=" + AVPtrIncrement + "\nLoc=" +
14772 // (AVPtr + AVPtrIncrement)->LocationName + "\nCmd=" + (AVPtr + AVPtrIncrement)->Command + "\nArr=" + ArrTime + "\nDep=" + DepTime + "\nEv=" + EvTime +
14773 // "\nDTAVPtr=" + DepTimeFromAVPtr + "\nDTListBox=" + DepTimeFromLB); //diagnostics
14774 
14775  }
14777  Utilities->CallLogPop(2697);
14778  }
14779  catch(const Exception &e)
14780  {
14781  ErrorLog(249, e.Message);
14782  }
14783 }
14784 
14785 //---------------------------------------------------------------------------
14786 
14787 void TInterface::ShowTTActionsListBox(int Caller) //TTClock stopped in ClockTimer2 by setting WarningHover to true
14788 {
14789  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ShowTTActionsListBox");
14790  if(SkipTTActionsListBox->Visible)
14791  {
14792  Utilities->CallLogPop(2430);
14793  return;
14794  }
14795  SkipTTActionsListBox->Visible = true;
14796  SkipListHeaderPanel->Visible = true;
14797  SkipTTActionsListBox->BringToFront();
14798  SkipListHeaderPanel->BringToFront();
14799  Utilities->CallLogPop(2431);
14800 }
14801 
14802 //---------------------------------------------------------------------------
14803 
14804 void TInterface::ShowReminderListBox(int Caller) //TTClock stopped in ClockTimer2 by setting WarningHover to true
14805 {
14806  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ShowReminderListBox");
14807  if(ReminderListBox->Visible)
14808  {
14809  Utilities->CallLogPop(2698);
14810  return;
14811  }
14812  ReminderListBox->Visible = true;
14813  ReminderHeaderPanel->Visible = true;
14814  ReminderListBox->BringToFront();
14815  ReminderHeaderPanel->BringToFront();
14816  Utilities->CallLogPop(2699);
14817 }
14818 
14819 //---------------------------------------------------------------------------
14820 
14822 {
14823  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",HideTTActionsListBox");
14824  if(!SkipTTActionsListBox->Visible)
14825  {
14826  Utilities->CallLogPop(2432);
14827  return;
14828  }
14829  SkipTTActionsListBox->Visible = false;
14830  SkipListHeaderPanel->Visible = false;
14831  Utilities->CallLogPop(2433);
14832 }
14833 
14834 //---------------------------------------------------------------------------
14835 
14836 void TInterface::HideReminderListBox(int Caller) //added at v2.19.0
14837 {
14838  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",HideReminderListBox");
14839  if(!ReminderListBox->Visible)
14840  {
14841  Utilities->CallLogPop(2700);
14842  return;
14843  }
14844  ReminderListBox->Visible = false;
14845  ReminderHeaderPanel->Visible = false;
14846  Utilities->CallLogPop(2701);
14847 }
14848 
14849 //---------------------------------------------------------------------------
14850 
14851 void __fastcall TInterface::SkipListExitImageClick(TObject *Sender)
14852 {
14853  try
14854  {
14855  TrainController->LogEvent("SkipListExitImageClick");
14856  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SkipListExitImageClick");
14858  Utilities->CallLogPop(2434);
14859  }
14860  catch(const Exception &e)
14861  {
14862  ErrorLog(243, e.Message);
14863  }
14864 }
14865 
14866 //---------------------------------------------------------------------------
14867 
14868 void __fastcall TInterface::ReminderExitImageClick(TObject *Sender) //added at v2.19.0
14869 {
14870  try
14871  {
14872  TrainController->LogEvent("ReminderExitImageClick");
14873  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ReminderExitImageClick");
14875  Utilities->CallLogPop(2702);
14876  }
14877  catch(const Exception &e)
14878  {
14879  ErrorLog(250, e.Message);
14880  }
14881 }
14882 
14883 //---------------------------------------------------------------------------
14884 
14885 void __fastcall TInterface::BecomeNewServiceMenuItemClick(TObject *Sender) //added at v2.12.0
14886 {
14887  //this is only accessible if the train is stopped at a location, there is a follow-on service (Fns, Fns-sh, Frh-sh, F-nshs) and that service stops at or passes the current
14888  // location. Change to new service immediately & set its ActionVectorEntryPtr to this location
14889  try
14890  {
14891  TrainController->LogEvent("BecomeNewServiceClick");
14892  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BecomeNewServiceClick");
14894  TActionVectorEntry *NewServiceActionEntryPtr = Train.ActionVectorEntryPtr; //set initially to current position
14895  while((NewServiceActionEntryPtr->Command != "Fns") && (NewServiceActionEntryPtr->Command != "Fns-sh") && (NewServiceActionEntryPtr->Command != "F-nshs") &&
14896  (NewServiceActionEntryPtr->Command != "Frh-sh")) //last condition added after v2.23.0 because of error reported by andre (andrek1410) via discord ticket #86
14897  { //error was that received the below message wrongly with plain shuttles without feeders
14898  NewServiceActionEntryPtr++;
14899  if(NewServiceActionEntryPtr > &Train.TrainDataEntryPtr->ActionVector.back()) //failed to find a new service
14900  {
14901  ShowMessage("No follow-on service found, option unavailable");
14902  Utilities->CallLogPop(2445);
14903  return;
14904  }
14905  }
14906  int PtrAdvance = NewServiceActionEntryPtr - Train.ActionVectorEntryPtr;
14907  AnsiString CurrentLocationName = Train.ActionVectorEntryPtr->LocationName;
14908  UnicodeString MessageStr = "This action will skip all timetabled events until follow-on service " + Train.FollowOnServiceRef + " reaches this location\n\nOK to proceed?";
14909  if(NewServiceActionEntryPtr->Command == "Fns")
14910  {
14911  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14913  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_YESNO);
14914  TrainController->BaseTime = TDateTime::CurrentDateTime();
14916  if(button == IDNO)
14917  {
14918  Train.FollowOnServiceRef = ""; //cancel this as finished with it
14919  Utilities->CallLogPop(2446);
14920  return;
14921  }
14923  Train.NewTrainService(1, true); //true for no logs
14924  }
14925  else if(NewServiceActionEntryPtr->Command == "Fns-sh")
14926  {
14927  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14929  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_YESNO);
14930  TrainController->BaseTime = TDateTime::CurrentDateTime();
14932  if(button == IDNO)
14933  {
14934  Train.FollowOnServiceRef = ""; //cancel this as finished with it
14935  Utilities->CallLogPop(2447);
14936  return;
14937  }
14939  Train.RepeatShuttleOrNewNonRepeatService(1, true); //true for no logs
14940  }
14941  else if(NewServiceActionEntryPtr->Command == "Frh-sh")
14942  {
14943  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14945  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_YESNO);
14946  TrainController->BaseTime = TDateTime::CurrentDateTime();
14948  if(button == IDNO)
14949  {
14950  Train.FollowOnServiceRef = ""; //cancel this as finished with it
14951  Utilities->CallLogPop(2453);
14952  return;
14953  }
14955  Train.RepeatShuttleOrRemainHere(1, true); //true for no logs;
14956  }
14957  else if(NewServiceActionEntryPtr->Command == "F-nshs")
14958  {
14959  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14961  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_YESNO);
14962  TrainController->BaseTime = TDateTime::CurrentDateTime();
14964  if(button == IDNO)
14965  {
14966  Train.FollowOnServiceRef = ""; //cancel this as finished with it
14967  Utilities->CallLogPop(2448);
14968  return;
14969  }
14971  Train.NewShuttleFromNonRepeatService(1, true); //true for no logs;
14972  }
14973  else //invalid, shouldn't have been possible to select it
14974  {
14975  ShowMessage("Option unavailable");
14976  }
14977 
14978  SkipEventsBeforeSameLoc(0, SelectedTrainID, CurrentLocationName); //same train ID when becomes new service
14979  Train.DepartureTimeSet = false; //force a release time calculation in UpdateTrain
14980  Train.EntrySpeed = 0;
14981  Train.ExitSpeedHalf = 0;
14982  Train.ExitSpeedFull = 0;
14983  if(Train.ActionVectorEntryPtr->Command == "pas")
14984  {
14985  Train.StoppedAtLocation = false; //this is set in the functions called for the various finish types, so is reset here in the case of a pass
14986  Train.ExitSpeedHalf = 30; //so earlier versions will still identify it as moving. exit speed will be recalculated for v2.12.0 & upwards
14987  Train.ExitSpeedFull = 50;
14988  }
14989  Train.FollowOnServiceRef = ""; //cancel this as finished with it
14990  Utilities->CallLogPop(2449);
14991  }
14992  catch(const Exception &e)
14993  {
14994  ErrorLog(244, e.Message);
14995  }
14996 }
14997 
14998 //---------------------------------------------------------------------------
14999 
15000 bool TInterface::IsBecomeNewServiceAvailable(int Caller, int TrainID, AnsiString &NextServiceRef)
15001 {// 5 conditions: stopped at location, has power, no skipped departure pending, has follow-on service (Fns, Fns-sh, Frh-sh, F-nshs) and follow-on service stops at or passes this location
15002  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ", BecomeNewServiceAvailable, " + AnsiString(TrainID));
15004  bool Condition4Met = false;
15005  TTrainDataEntry *NextServiceEntryPtr;
15006  NextServiceRef = "";
15007  if(Train.RevisedStoppedAtLoc() && (Train.PowerAtRail > 1) && !Train.SkippedDeparture) //conditions 1 to 3
15008  {
15009  for(TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr; AVEPtr <= &Train.TrainDataEntryPtr->ActionVector.back(); AVEPtr++)
15010  {
15011  if(AVEPtr->Command == "Fns")
15012  {
15013  Condition4Met = true;
15014  NextServiceEntryPtr = AVEPtr->LinkedTrainEntryPtr;
15015  NextServiceRef = TrainController->GetRepeatHeadCode(72, NextServiceEntryPtr->HeadCode, Train.RepeatNumber, Train.IncrementalDigits); //changed at v2.20.1 to give
15016  break; //correct headcode for repeats
15017  }
15018  if(AVEPtr->Command == "Fns-sh")
15019  {
15020  Condition4Met = true;
15021  if(Train.RepeatNumber >= (Train.TrainDataEntryPtr->NumberOfTrains - 1)) // finished all repeats
15022  {
15023  NextServiceEntryPtr = AVEPtr->NonRepeatingShuttleLinkEntryPtr;
15024  NextServiceRef = NextServiceEntryPtr->HeadCode; //no repeats for finishing service
15025  }
15026  else
15027  {
15028  NextServiceEntryPtr = AVEPtr->LinkedTrainEntryPtr;
15029  NextServiceRef = TrainController->GetRepeatHeadCode(73, NextServiceEntryPtr->HeadCode, Train.RepeatNumber + 1, Train.IncrementalDigits); //changed at v2.20.1 as above
15030  }
15031  break;
15032  }
15033  if(AVEPtr->Command == "Frh-sh")
15034  {
15035  if(Train.RepeatNumber >= (Train.TrainDataEntryPtr->NumberOfTrains - 1)) // finished all repeats
15036  {
15037  break; //no follow-on service
15038  }
15039  else
15040  {
15041  Condition4Met = true;
15042  NextServiceEntryPtr = AVEPtr->LinkedTrainEntryPtr;
15043  NextServiceRef = TrainController->GetRepeatHeadCode(74, NextServiceEntryPtr->HeadCode, Train.RepeatNumber + 1, Train.IncrementalDigits); //changed at v2.20.1 as above
15044  break;
15045  }
15046  }
15047  if(AVEPtr->Command == "F-nshs")
15048  {
15049  Condition4Met = true;
15050  NextServiceEntryPtr = AVEPtr->LinkedTrainEntryPtr;
15051  NextServiceRef = NextServiceEntryPtr->HeadCode; //no repeats for feeder service
15052  break;
15053  }
15054  }
15055  if(Condition4Met) //condition 4
15056  {
15057  AnsiString CurLoc = Train.ActionVectorEntryPtr->LocationName;
15058  for(TActionVectorEntry *AVEPtr = &NextServiceEntryPtr->ActionVector.at(0); AVEPtr <= &NextServiceEntryPtr->ActionVector.back(); AVEPtr++)
15059  {
15060  if(AVEPtr->LocationName == CurLoc)
15061  {
15062  if((AVEPtr->FormatType == TimeTimeLoc) || ((AVEPtr->FormatType == TimeLoc) && (AVEPtr->ArrivalTime > TDateTime(0))) || (AVEPtr->Command == "pas") )
15063  {
15064  Utilities->CallLogPop(2454);
15065  return(true); //all conditions met
15066  }
15067  }
15068  }
15069  }
15070  }
15071  Utilities->CallLogPop(2455);
15072  return(false); //one or more conditions not met
15073 }
15074 
15075 //---------------------------------------------------------------------------
15076 
15077 void TInterface::SkipAllEventsBeforeNewService(int Caller, int TrainID, int PtrAdvance) //added at v2.12.0
15078 {
15079  try
15080  {
15081  TrainController->LogEvent("SkipAllEventsBeforNewService");
15082  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ", SkipAllEventsBeforNewService");
15083  TTrain &Train = TrainController->TrainVectorAtIdent(59, TrainID);
15084  TActionVectorEntry *NewServiceActionEntryPtr = Train.ActionVectorEntryPtr + PtrAdvance;
15085  //Calc no. of skipped events
15086  int SkippedEvents = 0;
15087  for(TActionVectorEntry *AVEPtr = Train.ActionVectorEntryPtr; AVEPtr < NewServiceActionEntryPtr; AVEPtr++)
15088  {
15089  if((AVEPtr->Command == "cdt") || (AVEPtr->Command == "dsc") || (AVEPtr->Command == "cms") || (AVEPtr->Command == "pas") || (AVEPtr->SequenceType == FinishSequence) ||
15090  ((AVEPtr->FormatType == TimeLoc) && (AVEPtr->DepartureTime != TDateTime(-1))))
15091  //don't count cdts, dscs, cmss, passes, finishes or departures as missed events (finish will be the new service and becomes new service at diff loc so it isn't missed)
15092  {
15093  continue;
15094  }
15095  else
15096  {
15097  SkippedEvents++;
15098  }
15099  }
15100  //report the action
15101  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " manually advanced to follow-on service " + Train.FollowOnServiceRef + " at " +
15103  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
15104  Utilities->PerformanceFile.flush(); //added at v2.13.0
15105  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
15106  //advance the pointer
15107  Train.ActionVectorEntryPtr = NewServiceActionEntryPtr; //points to the new service
15108  PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " " + AnsiString(SkippedEvents)
15109  + " significant events skipped before it became the new service";
15110  TrainController->SkippedTTEvents += SkippedEvents;
15111  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
15112  Utilities->PerformanceFile.flush(); //added at v2.13.0
15113  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
15114  Utilities->CallLogPop(2450);
15115  return;
15116  }
15117  catch(const Exception &e)
15118  {
15119  ErrorLog(245, e.Message);
15120  }
15121 }
15122 
15123 //---------------------------------------------------------------------------
15124 
15125 void TInterface::SkipEventsBeforeSameLoc(int Caller, int TrainID, AnsiString LocationName) //added at v2.12.0
15126 {
15127  try
15128  {
15129  TrainController->LogEvent("SkipEventsBeforeSameLoc");
15130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ", SkipEventsBeforeSameLoc");
15131  TTrain &Train = TrainController->TrainVectorAtIdent(60, TrainID);
15132  bool LocFound = false;
15133  //Calc no. of skipped events
15134  int SkippedEvents = 0;
15135  TActionVectorEntry *AVEPtr;
15136  for(AVEPtr = Train.ActionVectorEntryPtr; AVEPtr <= &Train.TrainDataEntryPtr->ActionVector.back(); AVEPtr++)
15137  {
15138  if(AVEPtr->LocationName != LocationName)
15139  {
15140  if((AVEPtr->Command == "cdt") || (AVEPtr->Command == "dsc") || (AVEPtr->Command == "cms") || (AVEPtr->Command == "pas") || ((AVEPtr->FormatType == TimeLoc) && (AVEPtr->DepartureTime != TDateTime(-1))))
15141  //don't count cdts, dscs, cmss, passes or departures as missed events (can't be a finish)
15142  {
15143  continue;
15144  }
15145  else
15146  {
15147  SkippedEvents++;
15148  }
15149  }
15150  else if(AVEPtr->FormatType == TimeTimeLoc)
15151  {
15152  LocFound = true;
15153  break;
15154  }
15155  else if(AVEPtr->Command == "pas")
15156  {
15157  Train.TreatPassAsTimeLocDeparture = true; //when true the pas is treated by the train as a TimeLoc departure
15158  LocFound = true;
15159  break;
15160  }
15161  else if(((AVEPtr->FormatType == TimeLoc) && (AVEPtr->ArrivalTime > TDateTime(-1))) || (AVEPtr->Command == "cdt") || (AVEPtr->Command == "dsc") || (AVEPtr->Command == "cms"))
15162  { //increment past arrival, cdt, dsc & cms events, no skipped event as stops here
15163  continue;
15164  }
15165  else
15166  {
15167  LocFound = true;
15168  break;
15169  }
15170  }
15171  if(!LocFound)
15172  {
15173  Utilities->CallLogPop(2451);
15174  ShowMessage("New service location not found");
15175  return;
15176  }
15177  //advance the pointer
15178  Train.ActionVectorEntryPtr = AVEPtr;
15179  Train.ChangeTrainDirection(1, true); //most times this will be appropriate, i.e changed direction from other service train, true for No Logs
15180  AnsiString PerfStr = Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + Train.HeadCode + " " + AnsiString(SkippedEvents)
15181  + " significant events skipped before the new service arrived at " + LocationName;
15182  TrainController->SkippedTTEvents += SkippedEvents;
15183  Utilities->PerformanceFile << PerfStr.c_str() << '\n';
15184  Utilities->PerformanceFile.flush(); //added at v2.13.0
15185  PerfLogForm->PerformanceLogBox->Lines->Add(PerfStr);
15186  Utilities->CallLogPop(2452);
15187  return;
15188  }
15189  catch(const Exception &e)
15190  {
15191  ErrorLog(246, e.Message);
15192  }
15193 }
15194 
15195 //---------------------------------------------------------------------------
15196 
15197 void __fastcall TInterface::SignallerControlStopMenuItemClick(TObject *Sender)
15198 {
15199  try
15200  {
15201  TrainController->LogEvent("SignallerControlStopMenuItemClick");
15202  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SignallerControlStopMenuItemClick");
15205  if(Train.LeadElement > -1)
15206  {
15207  if(Track->TrackElementAt(787, Train.LeadElement).Conn[Train.LeadExitPos] > -1)
15208  {
15209  Train.SignallerStoppingFlag = true;
15210  Train.SignallerStopBrakeRate = 0;
15211  Train.LogAction(24, Train.HeadCode, "", SignallerControlStop, "", "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
15212  }
15213  else
15214  {
15216  }
15217  }
15218  else
15219  {
15221  }
15222  Utilities->CallLogPop(1553);
15223  }
15224  catch(const Exception &e)
15225  {
15226  ErrorLog(161, e.Message);
15227  }
15228 }
15229 
15230 // ---------------------------------------------------------------------------
15231 void __fastcall TInterface::PassRedSignalMenuItemClick(TObject *Sender)
15232 {
15233  try
15234  {
15235  TrainController->LogEvent("PassRedSignalMenuItemClick");
15236  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PassRedSignalMenuItemClick");
15238  if(Train.StoppedAtLocation && (Train.ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && !TrainLeaveWarningSent) //added at v2.14.0
15239  {
15240  UnicodeString MessageStr = "Please be aware that a train moved from a location prior to its departure time must be returned to that location "
15241  "in order to restore timetable control\n\nThis message will not be shown again\n\nOK to continue, Cancel to abort";
15242  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15244  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_OKCANCEL);
15245  TrainLeaveWarningSent = true;
15246  TrainController->BaseTime = TDateTime::CurrentDateTime();
15248  if(button == IDCANCEL)
15249  {
15250  Utilities->CallLogPop(2558);
15251  return;
15252  }
15253  }
15254  Train.SignallerStoppingFlag = false;
15255  int NextElementPos = Track->TrackElementAt(712, Train.LeadElement).Conn[Train.LeadExitPos];
15256  if(NextElementPos < 0)
15257  {
15258  throw Exception("Error, no element in front in PassRedSignalMenuItemClick");
15259  }
15260  TTrackElement &TrackElement = Track->TrackElementAt(653, NextElementPos);
15261 /* drop this error as may be some circumstances where behind a signal in sig mode but not stopped at signal
15262  if(!Train.StoppedAtSignal)
15263  {
15264  throw Exception("Error, not StoppedAtSignal in PassRedSignalMenuItemClick");
15265  }
15266 */
15267  if(TrackElement.TrackType != SignalPost)
15268  {
15269  throw Exception("Error, next element not a signal type in PassRedSignalMenuItemClick");
15270  }
15271  Train.SignallerStopped = false;
15272  Train.StoppedAtLocation = false; // may have started at station in signaller mode and also at a red signal, in this case both SignallerStopped
15273  // and StoppedAtLocation are set but the background colour stays pale green for station, not signal,
15274  // since no need to alert the user
15275  Train.StoppedAfterSPAD = false; // in case had been set
15276  Train.SPADFlag = false;
15277  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
15279  Train.AllowedToPassRedSignal = true;
15280  Train.LogAction(4, Train.HeadCode, "", SignallerPassRedSignal, "", "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
15281  Utilities->CallLogPop(1199);
15282  }
15283  catch(const Exception &e)
15284  {
15285  ErrorLog(162, e.Message);
15286  }
15287 }
15288 // ---------------------------------------------------------------------------
15289 
15290 void __fastcall TInterface::StepForwardMenuItemClick(TObject *Sender)
15291 {
15292  try
15293  {
15294  TrainController->LogEvent("StepForwardMenuItemClick");
15295  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",StepForwardMenuItemClick");
15297  if(Train.StoppedAtLocation && (Train.ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && !TrainLeaveWarningSent) //added at v2.14.0
15298  {
15299  UnicodeString MessageStr = "Please be aware that a train moved from a location prior to its departure time must be returned to (or still be at) that location "
15300  "in order to restore timetable control\n\nThis message will not be shown again\n\nOK to continue, Cancel to abort";
15301  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15303  int button = Application->MessageBox(MessageStr.c_str(), L"", MB_OKCANCEL);
15304  TrainLeaveWarningSent = true;
15305  TrainController->BaseTime = TDateTime::CurrentDateTime();
15307  if(button == IDCANCEL)
15308  {
15309  Utilities->CallLogPop(2559);
15310  return;
15311  }
15312  }
15313  Train.SignallerStoppingFlag = false;
15314  Train.SignallerStopped = false;
15315  Train.StoppedAtLocation = false; // may have started at station in signaller mode and also at a red signal, in this case both SignallerStopped
15316  // and StoppedAtLocation are set but the background colour stays pale green for station, not signal,
15317  // since no need to alert the user
15318  Train.StoppedAfterSPAD = false; // in case had been set
15319  Train.SPADFlag = false;
15320  Train.StepForwardFlag = true;
15321  Train.AllowedToPassRedSignal = true; // in case at a signal, will clear when half-way into next element whether a signal or not
15322  // ok to call PlotTrainWithNewBackgroundColour here as PlotElements already set to Lead, Mid & Lag elements
15324  Train.LogAction(32, Train.HeadCode, "", SignallerStepForward, "", "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
15325  int NextElementPos = -1;
15326 // addition for v1.3.2 due to Carwyn Thomas error: can't select StepForwardMenuItem if exiting at a continuation but leave this in anyway
15327  int NextEntryPos = -1; // ---ditto---
15328  if(Train.LeadElement > -1) // ---ditto---
15329  {
15330  // ---ditto---
15331  NextElementPos = Track->TrackElementAt(804, Train.LeadElement).Conn[Train.LeadExitPos]; // had 'int' prefix before additions
15332  NextEntryPos = Track->TrackElementAt(805, Train.LeadElement).ConnLinkPos[Train.LeadExitPos]; // ---ditto---
15333  } // ---ditto---
15334 
15335  if((NextElementPos > -1) && (NextEntryPos > -1))
15336  {
15337  // call this after StepForwardFlag set
15338  Train.EntrySpeed = 0;
15340  Train.FirstHalfMove = true;
15341  Train.SetTrainMovementValues(20, NextElementPos, NextEntryPos); // NextElement is the element to be entered
15342  }
15343  Utilities->CallLogPop(1800);
15344  }
15345  catch(const Exception &e)
15346  {
15347  ErrorLog(163, e.Message);
15348  }
15349 }
15350 
15351 // ---------------------------------------------------------------------------
15352 
15353 void __fastcall TInterface::RemoveTrainMenuItemClick(TObject *Sender)
15354 {
15355  try
15356  {
15357  TrainController->LogEvent("RemoveTrainMenuItemClick");
15358  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RemoveTrainMenuItemClick");
15360  if((!Train.Derailed) && (!Train.Crashed))
15361  {
15362  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15364  UnicodeString Message = UnicodeString(Train.HeadCode) + " will be removed from the railway - proceed?";
15365  int button = Application->MessageBox(Message.c_str(), L"Please confirm", MB_YESNO);
15366  TrainController->BaseTime = TDateTime::CurrentDateTime();
15368  if(button == IDNO)
15369  {
15370  Utilities->CallLogPop(1801);
15371  return;
15372  }
15373  }
15374  Train.SignallerStoppingFlag = false;
15375  Train.TrainGone = true; // will be removed by TTrainController::Operate
15376  Train.SignallerRemoved = true;
15377  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
15378  AnsiString LocName = "";
15379  if(Train.LeadElement > -1)
15380  {
15381  LocName = Track->TrackElementAt(641, Train.LeadElement).ActiveTrackElementName;
15382  }
15383  if((LocName == "") && (Train.MidElement > -1))
15384  {
15385  LocName = Track->TrackElementAt(642, Train.MidElement).ActiveTrackElementName;
15386  }
15387  if((LocName == "") && Train.LeadElement > -1)
15388  {
15389  LocName = Track->TrackElementAt(643, Train.LeadElement).ElementID;
15390  }
15391  if((LocName == "") && (Train.MidElement > -1))
15392  {
15393  LocName = Track->TrackElementAt(644, Train.MidElement).ElementID;
15394  }
15395  TTrackElement *TrackElementPtr;
15396  int RouteNumber;
15397  TAllRoutes::TRouteType RouteType;
15398  if(Train.LeadElement > -1)
15399  {
15400  TrackElementPtr = &(Track->TrackElementAt(673, Train.LeadElement));
15401  // remove TrainIDs from track element, added at v2.4.0
15402  if(TrackElementPtr->TrackType == Bridge)
15403  {
15404  if(Train.LeadExitPos > 1)
15405  {
15406  TrackElementPtr->TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
15407  }
15408  else
15409  {
15410  TrackElementPtr->TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
15411  }
15412  }
15413  else
15414  {
15415 // TrackElementPtr->TrainIDOnElement = -1; //dropped at v2.15.1, this needs to stay until removed during TTrainController::Operate
15416  //else another train can enter, e.g. if removed from a continuation when other trains overdue for entry,
15417  //this happened (for LeadElement > -1) when testing Crewe when a subsequent train entered, then
15418  //the ID was removed when the removed train next cleared it during TTrainController::Operate allowing
15419  //another train to enter, and the first train that entered crashed when it tried to enter fully onto
15420  //the continuation.
15421  }
15422  // reset any CallingOnSet flags for signals, if facing wrong way doesn't matter, shouldn't be set anyay
15423  if((TrackElementPtr->TrackType == SignalPost) && TrackElementPtr->CallingOnSet)
15424  {
15425  TrackElementPtr->CallingOnSet = false;
15426  Track->PlotSignal(6, *TrackElementPtr, Display);
15427  }
15428 // [added at v1.3.0] here check if on an automatic signals route and if so reset signals for the entire route from the
15429 // start of the route - after the train has been removed, use LeadElement and also MidElement (if no autosigs route at LeadElement) just to be sure
15430  RouteType = AllRoutes->GetRouteTypeAndNumber(27, Train.LeadElement, Train.LeadEntryPos, RouteNumber);
15431  if(RouteType == TAllRoutes::AutoSigsRoute)
15432  {
15435  }
15436 // end of addition
15437 
15438 // erase a stub route if there is one, added at v2.6.1
15439 // first element of route is immediately in front of the train
15440  if(Train.LeadExitPos >= 0)
15441  {
15442  TTrackElement LeadTrackElement = Track->TrackElementAt(1013, Train.LeadElement);
15443  int FirstRouteElementVecPos = LeadTrackElement.Conn[Train.LeadExitPos];
15444  int FirstRouteLinkPos = LeadTrackElement.ConnLinkPos[Train.LeadExitPos];
15445  RouteType = AllRoutes->GetRouteTypeAndNumber(39, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
15446  if(RouteType == TAllRoutes::NotAutoSigsRoute) // red or green route, if no route then ignore
15447  {
15448  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(30, RouteNumber);
15449  TTrackElement TE = Track->TrackElementAt(1014, FirstRouteElementVecPos);
15450  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))
15451  // all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
15452  {
15453  bool FirstPass = true; //added at v2.8.0
15454  while(OR.PrefDirSize() > 0)
15455  // remove the route up to but not including the next facing signal, in case a route extends to another signal
15456  {
15457  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(249, 0);
15458 // these will change at each element removal because OR is a reference to the real route
15459  int TVPos2 = PDE.GetTrackVectorPosition();
15460  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
15461  {
15462  break;
15463  }
15464  TTrackElement TE2 = Track->TrackElementAt(1015, TVPos2);
15465  if(Track->TrackElementAt(1016, PDE.GetTrackVectorPosition()).Config[PDE.GetXLinkPos()] != Signal)
15466  {
15467  AllRoutes->RemoveRouteElement(24, TE2.HLoc, TE2.VLoc, PDE.GetELink());
15468  }
15469  else
15470  {
15471  break;
15472  }
15473  FirstPass = false;
15474  }
15475  AllRoutes->RebuildRailwayFlag = true;
15476  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
15477  }
15478  }
15479  }
15480 // end of stub route removal addition
15481  }
15482  if(Train.MidElement > -1)
15483  {
15484  TrackElementPtr = &(Track->TrackElementAt(674, Train.MidElement));
15485  // remove TrainIDs from track element, added at v2.4.0
15486  if(TrackElementPtr->TrackType == Bridge)
15487  {
15488  if(Train.MidExitPos > 1)
15489  {
15490  TrackElementPtr->TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
15491  }
15492  else
15493  {
15494  TrackElementPtr->TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
15495  }
15496  }
15497  else
15498  {
15499 // TrackElementPtr->TrainIDOnElement = -1; //dropped at v2.15.1, this needs to stay until removed during TTrainController::Operate
15500  //else another train can enter, e.g. if removed from a continuation when other trains overdue for entry,
15501  //this happened (for leadElement - see above) when testing Crewe when a subsequent train entered, then
15502  //the ID was removed when the removed train next cleared it during TTrainController::Operate allowing
15503  //another train to enter, and the first train that entered crashed when it tried to enter fully onto
15504  //the continuation.
15505  }
15506  if((TrackElementPtr->TrackType == SignalPost) && TrackElementPtr->CallingOnSet)
15507  {
15508  TrackElementPtr->CallingOnSet = false;
15509  Track->PlotSignal(7, *TrackElementPtr, Display);
15510  }
15511 // [added at v1.3.0 as above]
15513  {
15514  RouteType = AllRoutes->GetRouteTypeAndNumber(28, Train.MidElement, Train.MidEntryPos, RouteNumber);
15515  if(RouteType == TAllRoutes::AutoSigsRoute)
15516  {
15519  }
15520  }
15521 // end of addition
15522  }
15523  if(Train.LeadElement > -1)
15524  // addition for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at next line
15525  {
15526  if(Track->TrackElementAt(675, Train.LeadElement).Conn[Train.LeadExitPos] > -1)
15527  {
15528  TrackElementPtr = &(Track->TrackElementAt(676, Track->TrackElementAt(677, Train.LeadElement).Conn[Train.LeadExitPos]));
15529  if((TrackElementPtr->TrackType == SignalPost) && TrackElementPtr->CallingOnSet)
15530  {
15531  TrackElementPtr->CallingOnSet = false;
15532  Track->PlotSignal(8, *TrackElementPtr, Display);
15533  }
15534  }
15535  }
15536  Train.LogAction(5, Train.HeadCode, "", RemoveTrain, LocName, "", TDateTime(0), false); // TDateTime is a dummy entry, false for no warning
15537  if(Train.ActionVectorEntryPtr->Command != "Frh") // if remaining at location no point in sending 'failed to terminate' message
15538  {
15539  Train.SendMissedActionLogs(0, -1, Train.ActionVectorEntryPtr); // -1 is a marker for send messages for all remaining
15540  } // entries, including Fer if present
15541  if(Train.LongServRefEnteredFlag)
15542  {
15544  }
15545  Utilities->CallLogPop(1200);
15546  }
15547  catch(const Exception &e)
15548  {
15549  ErrorLog(164, e.Message);
15550  }
15551 }
15552 
15553 // ---------------------------------------------------------------------------
15554 
15555 void __fastcall TInterface::ErrorButtonClick(TObject *Sender)
15556 // to terminate after error message given
15557 {
15558  ErrorMessage->Visible = false;
15559  ErrorMessageStoreImage->Visible = false;
15560  ErrorButton->Visible = false;
15561  Display->GetImage()->Visible = true;
15562  Application->Terminate();
15563 }
15564 
15565 // ---------------------------------------------------------------------------
15566 
15567 void __fastcall TInterface::FormClose(TObject *Sender, TCloseAction &Action)
15568 {
15569  try
15570  {
15571  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",FormClose");
15572 /* Dropped at v2.9.1 as serves no apparent purpose
15573  if(!FileChangedFlag && !(Track->IsTrackFinished()) && (EveryPrefDir->PrefDirSize() > 0))
15574  {
15575  UnicodeString MessageStr =
15576  "Note that leaving the track unlinked will cause preferred directions to be lost on reloading. Prevent by linking the track then resaving. Do you still wish to exit?";
15577  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
15578  if(button == IDNO)
15579  {
15580  Action = caNone; // prevents form & application from closing
15581  Utilities->CallLogPop(1712);
15582  return;
15583  }
15584  }
15585 */
15587  {
15588  UnicodeString MessStr = "";
15590  {
15591  MessStr = UnicodeString("The railway and the timetable have both changed, exit without saving either?");
15592  }
15593  else if(FileChangedFlag)
15594  {
15595  MessStr = UnicodeString("The railway has changed, exit without saving?");
15596  }
15597  else
15598  {
15599  MessStr = UnicodeString("The timetable has changed, exit without saving?");
15600  }
15601  int button = Application->MessageBox(MessStr.c_str(), L"Please confirm", MB_YESNO);
15602  if(button == IDNO)
15603  {
15604  Action = caNone; // prevents form & application from closing
15605  Utilities->CallLogPop(1133);
15606  return;
15607  }
15608  }
15609  if(Level1Mode == OperMode)
15610  {
15612  {
15613  UnicodeString MessageStr = "Please note that the session will be lost if it hasn't been saved. Do you still wish to exit?";
15614  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15616  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
15617  TrainController->BaseTime = TDateTime::CurrentDateTime();
15619  if(button == IDNO)
15620  {
15621  Action = caNone; // prevents form & application from closing
15622  Utilities->CallLogPop(969);
15623  return;
15624  }
15625  }
15626  PerfLogForm->Close();
15628  Utilities->PerformanceFile.close();
15629  }
15630  if((TempTTFileName != "") && FileExists(TempTTFileName))
15631  {
15632  DeleteFile(TempTTFileName);
15633  }
15634  Utilities->CallLogPop(971);
15635  }
15636  catch(const Exception &e)
15637  {
15638  ErrorLog(166, e.Message);
15639  }
15640 }
15641 
15642 // ---------------------------------------------------------------------------
15643 
15644 void __fastcall TInterface::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
15645 {
15646 // TrainController->LogEvent("FormKeyDown," + AnsiString(Key));
15647 // drop event log as have too many spurious entries
15648  try
15649  {
15650  if((Shift.Contains(ssAlt)) && (Shift.Contains(ssCtrl)))
15651  {
15652  if(Key == '2')
15653  {
15654  if(CallLogTickerLabel->Visible)
15655  {
15656  CallLogTickerLabel->Visible = false;
15657  }
15658  else
15659  {
15660  CallLogTickerLabel->Visible = true;
15661  }
15662  }
15663  else if(Key == '3')
15664  {
15665  if(DevelopmentPanel->Visible)
15666  {
15667  DevelopmentPanel->Visible = false;
15668  }
15669  else
15670  {
15671  DevelopmentPanel->Visible = true;
15672  DevelopmentPanel->BringToFront();
15673  }
15674  }
15675  else if(Key == '4')
15676  {
15677  TestFunction();
15678  }
15679  else if(Key == '5')
15680  {
15681  TMsgDlgButtons Buttons;
15682  Buttons << mbYes << mbNo;
15683  if(MessageDlg("Do you wish to allow signals next to bridges? If so please be aware that routes cannot be truncated to these signals.",
15684  mtWarning, Buttons, 0) == mrYes)
15685  {
15687  }
15688  else
15689  {
15691  }
15692  }
15693  }
15694  else if(Shift.Contains(ssCtrl) && !Shift.Contains(ssShift) && !Shift.Contains(ssAlt))
15695  {
15696  CtrlKey = true;
15697  }
15698  else if(Shift.Contains(ssShift) && !Shift.Contains(ssCtrl) && !Shift.Contains(ssAlt))
15699  {
15700  ShiftKey = true;
15701  }
15702 // below added at v1.3.0 to allow keyboard scrolling as well as mouse button scrolling - see user suggestion on Features & Requests forum 30/09/12
15703 // the NonCTRLOrSHIFTKeyUpFlag prevents repeated viewpoint movements without keys being re-pressed
15704 // note that use the OnKeyDown event rather than OnKeyPress as suggested by the user so that the CTRL & SHIFT keys can be taken into account
15705 
15706 // at v2.4.2 the flag changed to LastNonCtrlOrShiftKeyDown to make condition specific to last key, because when a message given the key up event
15707 // is not seen as the form does not have focus, so with the flag no shortcut key will work on the first press, with this only the same shortcut key
15708 // won't work on first press and that is less likely to be used a second time on either side of the message
15709 
15710  if((Key != VK_SHIFT) && (Key != VK_CONTROL))
15711  {
15712  if(LastNonCtrlOrShiftKeyDown == Key) // same key still down rejected
15713  {
15714  return;
15715  }
15716  else
15717  {
15719  }
15720  }
15721  if(Key == VK_UP)
15722  {
15723  if(ScreenUpButton->Enabled)
15724  {
15725  ScreenUpButton->Click();
15726  }
15727  }
15728  else if(Key == VK_DOWN)
15729  {
15730  if(ScreenDownButton->Enabled)
15731  {
15732  ScreenDownButton->Click();
15733  }
15734  }
15735  else if(Key == VK_LEFT)
15736  {
15737  if(ScreenLeftButton->Enabled)
15738  {
15739  ScreenLeftButton->Click();
15740  }
15741  }
15742  else if(Key == VK_RIGHT)
15743  {
15744  if(ScreenRightButton->Enabled)
15745  {
15746  ScreenRightButton->Click();
15747  }
15748  }
15749  else if(Key == VK_HOME)
15750  {
15751  if(HomeButton->Enabled)
15752  {
15753  HomeButton->Click();
15754  }
15755  }
15756 // end of 1.3.0 addition
15757  else if(Key == VK_END) // added at v2.2.0 to toggle zoom using 'End' key
15758  {
15759  if(ZoomButton->Enabled)
15760  {
15761  ZoomButton->Click();
15762  }
15763  }
15764  else if(Key == VK_END) // added at v2.2.0 to toggle zoom using 'End' key
15765  {
15766  if(ZoomButton->Enabled)
15767  {
15768  ZoomButton->Click();
15769  }
15770  }
15771 // below added for v2.4.2 to add more keyboard shortcuts
15772  if(DistanceBox->Focused() || SpeedLimitBox->Focused() || MileEdit->Focused() || ChainEdit->Focused() || YardEdit->Focused() ||
15773  SpeedEditBox2->Focused() || MTBFEditBox->Focused() || LocationNameTextBox->Focused() || TextBox->Focused() || PowerEditBox->Focused() ||
15774  SpeedEditBox->Focused() || AddSubMinsBox->Focused() || OneEntryTimetableMemo->Focused())
15775  {
15776  // prevent letter keys interfering when these boxes have focus - many are mutually exclusive but include them all
15777  return;
15778  }
15779  if(Shift.Contains(ssShift) && !Shift.Contains(ssAlt) && !Shift.Contains(ssCtrl) && NewHomeButton->Enabled && NewHomeButton->Visible)
15780  {
15781  if(Level1Mode != TimetableMode && (Key == 'H' || Key == 'h')) // TimetablePanel uses Shift H too so disable this when it's in use
15782  {
15783  NewHomeButton->Click();
15784  }
15785  }
15786 // Operating panel
15787  if(Level1Mode == OperMode && OperatingPanel->Enabled && OperatingPanel->Visible && !Shift.Contains(ssShift) && !Shift.Contains(ssAlt) &&
15788  !MultiplayerPlayerPanel->Visible) //last condition added as these letters can be added in the edit boxes
15789  {
15790  // use Shift.Contains(ssShift etc instead of ShiftKey as that not set if pressed second after Ctrl key pressed
15791  if(!Shift.Contains(ssCtrl))
15792  {
15793  if(OperateButton->Visible && OperateButton->Enabled)
15794  {
15795  if(Level2OperMode == Operating && (Key == 'P' || Key == 'p'))
15796  {
15797  OperateButton->Click();
15798  }
15799  else if((Level2OperMode == Paused || Level2OperMode == PreStart) && (Key == 'R' || Key == 'r'))
15800  {
15801  OperateButton->Click();
15802  }
15803  }
15804  if(PresetAutoSigRoutesButton->Visible && PresetAutoSigRoutesButton->Enabled && (Key == 'A' || Key == 'a'))
15805  {
15806  PresetAutoSigRoutesButton->Click();
15807  }
15808  if(PerformanceLogButton->Visible && PerformanceLogButton->Enabled && (Key == 'L' || Key == 'l'))
15809  {
15810  PerformanceLogButton->Click();
15811  }
15812  if(CallingOnButton->Visible && CallingOnButton->Enabled && (Key == 'O' || Key == 'o'))
15813  {
15814  CallingOnButton->Down = true; //added at v2.15.0 as key doesn't work without this - don't understand why
15815  CallingOnButton->Click();
15816  }
15817  if(OperatorActionButton->Visible && OperatorActionButton->Enabled && (Key == 'D' || Key == 'd'))
15818  {
15819  OperatorActionButton->Click();
15820  }
15821  if(RouteCancelButton->Visible && RouteCancelButton->Enabled && (Key == 'C' || Key == 'c'))
15822  {
15823  RouteCancelButton->Click();
15824  }
15825  if(TTClockAdjButton->Visible && TTClockAdjButton->Enabled && (Key == 'T' || Key == 't'))
15826  {
15827  TTClockAdjButton->Click();
15828  }
15829  if(AutoSigsButton->Visible && AutoSigsButton->Enabled && Key == '1') // route buttons - autosigs consec
15830  {
15831  AutoSigsButton->Click();
15832  }
15833  if(SigAutoNonConsecButton->Visible && SigAutoNonConsecButton->Enabled && Key == '4') // route buttons - autosigs non-consec added at v2.14.0
15834  {
15835  SigAutoNonConsecButton->Click();
15836  }
15837  if(SigPrefConsecButton->Visible && SigPrefConsecButton->Enabled && Key == '2') // route buttons - prefdir consec
15838  {
15839  SigPrefConsecButton->Click();
15840  }
15841  if(SigPrefNonConsecButton->Visible && SigPrefNonConsecButton->Enabled && Key == '5') // added at v2.7.0 for prefdir & any following signal
15842  {
15843  SigPrefNonConsecButton->Click();
15844  }
15845  if(UnrestrictedButton->Visible && UnrestrictedButton->Enabled && Key == '3') // route buttons - unrestricted
15846  {
15847  UnrestrictedButton->Click();
15848  }
15849  if(ExitOperationButton->Visible && ExitOperationButton->Enabled && Key == '\x1b')
15850  {
15851  ExitOperationButton->Click();
15852  }
15853  }
15854  else // CtrlKey down
15855  {
15856  if(SaveSessionButton->Visible && SaveSessionButton->Enabled)
15857  {
15858  SaveMenuItem->ShortCut = 0;
15859 // It's normally 16467 (Ctrl S) & will be restored in FormKeyUp. Has to be reset or will grab the key combination
15860  if(Key == 'S' || Key == 's') // so this will never execute
15861  {
15862  SaveSessionButton->Click();
15863  }
15864  }
15865  }
15866  }
15867 // Timetable clock adjust panel
15868  if(Level1Mode == OperMode && TTClockAdjPanel->Enabled && TTClockAdjPanel->Visible && !Shift.Contains(ssCtrl) && !Shift.Contains(ssAlt))
15869  {
15870  // use Shift.Contains(ssShift etc instead of ShiftKey as that not set if pressed second after Ctrl key pressed
15871  if(Shift.Contains(ssShift))
15872  {
15873  if(TTClockExitButton->Visible && TTClockExitButton->Enabled && (Key == 'A' || Key == 'a'))
15874  {
15875  TTClockExitButton->Click();
15876  }
15877  if(TTClockResetButton->Visible && TTClockResetButton->Enabled && (Key == 'R' || Key == 'r'))
15878  {
15879  TTClockResetButton->Click();
15880  }
15881  }
15882  }
15883 // Track build panel
15884  if((Level1Mode == TrackMode) && TrackBuildPanel->Visible && TrackBuildPanel->Enabled)
15885  {
15886  if(Shift.Contains(ssShift) && !Shift.Contains(ssCtrl) && !Shift.Contains(ssAlt))
15887  {
15888  if(AddTrackButton->Visible && AddTrackButton->Enabled && (Key == 'A' || Key == 'a')) // add/remove track elements
15889  {
15890  AddTrackButton->Click();
15891  }
15892  if(SigAspectButton->Visible && SigAspectButton->Enabled && (Key == 'S' || Key == 's')) // cycle through signal aspects
15893  {
15894  SigAspectButton->Click();
15895  }
15896  if(TrackOKButton->Visible && TrackOKButton->Enabled && (Key == 'L' || Key == 'l')) // link track
15897  {
15898  TrackOKButton->Click();
15899  }
15900  if(FontButton->Visible && FontButton->Enabled && (Key == 'F' || Key == 'f')) // change font
15901  {
15902  FontButton->Click();
15903  }
15904  if(LocationNameButton->Visible && LocationNameButton->Enabled && (Key == 'N' || Key == 'n')) // name locations
15905  {
15906  LocationNameButton->Click();
15907  }
15908  if(SetLengthsButton->Visible && SetLengthsButton->Enabled && (Key == 'D' || Key == 'd')) // set distances/speeds
15909  {
15910  SetLengthsButton->Click();
15911  }
15912  if(AddTextButton->Visible && AddTextButton->Enabled && (Key == 'T' || Key == 't')) // add text
15913  {
15914  AddTextButton->Click();
15915  }
15916  if(ScreenGridButton->Visible && ScreenGridButton->Enabled && (Key == 'G' || Key == 'g')) // toggle grid
15917  {
15918  ScreenGridButton->Click();
15919  }
15920  if(MoveTextOrGraphicButton->Visible && MoveTextOrGraphicButton->Enabled && (Key == 'M' || Key == 'm')) // move text or graphic
15921  {
15922  MoveTextOrGraphicButton->Click();
15923  }
15924  if(UserGraphicButton->Visible && UserGraphicButton->Enabled && (Key == 'I' || Key == 'i')) // insert image
15925  {
15926  UserGraphicButton->Click();
15927  }
15928  if(SetGapsButton->Visible && SetGapsButton->Enabled && (Key == 'J' || Key == 'j')) // join gaps
15929  {
15930  SetGapsButton->Click();
15931  }
15932  }
15933  if(Shift.Contains(ssCtrl) && !Shift.Contains(ssShift) && !Shift.Contains(ssAlt))
15934  {
15935  if(SaveRailwayTBPButton->Visible && SaveRailwayTBPButton->Enabled) // save railway in trackbuild mode
15936  {
15937  SaveMenuItem->ShortCut = 0;
15938 // It's normally 16467 (Ctrl S) & will be restored in FormKeyUp. Has to be reset or will grab the key combination
15939  if(Key == 'S' || Key == 's') // so this will never execute
15940  {
15941  SaveRailwayTBPButton->Click();
15942  }
15943  }
15944  }
15945  if(!Shift.Contains(ssCtrl) && !Shift.Contains(ssShift) && !Shift.Contains(ssAlt))
15946  {
15947  if((ExitTrackButton->Visible && ExitTrackButton->Enabled) && Key == '\x1b') // escape key
15948  {
15949  ExitTrackButton->Click();
15950  }
15951  }
15952  }
15953 // PrefDir panel
15954  if(Level1Mode == PrefDirMode && PrefDirPanel->Visible && PrefDirPanel->Enabled && !Shift.Contains(ssAlt))
15955  {
15956  if(!Shift.Contains(ssShift) && !Shift.Contains(ssCtrl))
15957  {
15958  if((ExitPrefDirButton->Visible && ExitPrefDirButton->Enabled) && Key == '\x1b') // escape key
15959  {
15960  ExitPrefDirButton->Click();
15961  }
15962  }
15963  if(!Shift.Contains(ssShift) && Shift.Contains(ssCtrl))
15964  {
15965  if(SaveRailwayPDPButton->Visible && SaveRailwayPDPButton->Enabled)
15966  {
15967  SaveMenuItem->ShortCut = 0;
15968 // It's normally 16467 (Ctrl S) & will be restored in FormKeyUp. Has to be reset or will grab the key combination
15969  if(Key == 'S' || Key == 's') // so this will never execute
15970  {
15971  SaveRailwayPDPButton->Click();
15972  }
15973  }
15974  }
15975  if(Shift.Contains(ssShift) && !Shift.Contains(ssCtrl))
15976  {
15977  if(AddPrefDirButton->Visible && AddPrefDirButton->Enabled && (Key == 'A' || Key == 'a')) // add pref dir
15978  {
15979  AddPrefDirButton->Click();
15980  }
15981  if(DeleteOnePrefDirButton->Visible && DeleteOnePrefDirButton->Enabled && (Key == 'D' || Key == 'd')) // delete one pref dir
15982  {
15983  DeleteOnePrefDirButton->Click();
15984  }
15985  if(DeleteAllPrefDirButton->Visible && DeleteAllPrefDirButton->Enabled && (Key == 'C' || Key == 'c')) // delete all pref dirs
15986  {
15987  DeleteAllPrefDirButton->Click();
15988  }
15989  }
15990  }
15991 // Note that save button in BaseMode is handled by Ctrl S from the File menu
15992 
15993 // Timetable panel
15994  if(Level1Mode == TimetableMode && TimetablePanel->Visible && TimetablePanel->Enabled && !Shift.Contains(ssAlt))
15995  {
15996  if(!Shift.Contains(ssShift) && !Shift.Contains(ssCtrl))
15997  {
15998  if(ExitTTModeButton->Visible && ExitTTModeButton->Enabled && Key == '\x1b') // escape key
15999  {
16000  ExitTTModeButton->Click();
16001  }
16002  }
16003  if(Shift.Contains(ssShift) && !Shift.Contains(ssCtrl)) // show/hide timetable edit panel
16004  {
16005  if(ShowHideTTButton->Visible && ShowHideTTButton->Enabled)
16006  {
16007  if(!TimetableEditPanel->Visible)
16008  {
16009  if(Key == 'S' || Key == 's')
16010  {
16011  ShowHideTTButton->Click();
16012  }
16013  }
16014  else if(Key == 'H' || Key == 'h')
16015  {
16016  ShowHideTTButton->Click();
16017  }
16018  }
16019  }
16020  }
16021 // Timetable edit panel
16022 // These just set flags. The corresponding 'Click()' function executes separately to the keypress because Windows stores the key until after any directly linked key code
16023 // is executed then selects the timetable entry that begins with the letter corresponding to the key. Without this separation the list box is left with the wrong entry
16024 // showing. See DevHistory.txt for the version at v2.5.0 for details.
16025  if(Level1Mode == TimetableMode && TimetableEditPanel->Visible && TimetableEditPanel->Enabled && !Shift.Contains(ssAlt))
16026  {
16027  if(Shift.Contains(ssShift) && !Shift.Contains(ssCtrl))
16028  {
16030 // store value here before the Windows key press function runs (it runs after any local code)
16031  if(PreviousTTEntryButton->Enabled && (Key == 'L' || Key == 'l'))
16032  {
16033  PreviousTTEntryKeyFlag = true;
16034  }
16035  if(NextTTEntryButton->Enabled && (Key == 'N' || Key == 'n'))
16036  {
16037  NextTTEntryKeyFlag = true;
16038  }
16039  if(MoveTTEntryUpButton->Enabled && (Key == 'U' || Key == 'u'))
16040  {
16041  MoveTTEntryUpKeyFlag = true;
16042  }
16043  if(MoveTTEntryDownButton->Enabled && (Key == 'D' || Key == 'd'))
16044  {
16045  MoveTTEntryDownKeyFlag = true;
16046  }
16047  if(CopyTTEntryButton->Enabled && (Key == 'C' || Key == 'c'))
16048  {
16049  CopyTTEntryKeyFlag = true;
16050  }
16051  if(InvertTTEntryButton->Enabled && (Key == 'J' || Key == 'j'))
16052  {
16053  InvertTTEntryKeyFlag = true;
16054  }
16055  if(CutTTEntryButton->Enabled && (Key == 'X' || Key == 'x'))
16056  {
16057  CutTTEntryKeyFlag = true;
16058  }
16059  if(PasteTTEntryButton->Enabled && (Key == 'P' || Key == 'p'))
16060  {
16061  PasteTTEntryKeyFlag = true;
16062  }
16063  if(DeleteTTEntryButton->Enabled && (Key == 'E' || Key == 'e'))
16064  {
16065  DeleteTTEntryKeyFlag = true;
16066  }
16067 /* if(SaveTTEntryButton->Enabled && (Key == 'E' || Key == 'e')) //can't have save while editing entry as adds the letter to the entry
16068  {
16069  SaveTTEntryKeyFlag = true;
16070  }
16071  if(CancelTTActionButton->Enabled && (Key == 'K' || Key == 'k')) //can't have cancel while editing entry as adds the letter to the entry
16072  {
16073  CancelTTActionKeyFlag = true;
16074  }
16075 */
16076  if(NewTTEntryButton->Enabled && (Key == 'I' || Key == 'i'))
16077  {
16078  NewTTEntryKeyFlag = true;
16079  }
16080  if(AZOrderButton->Enabled && (Key == 'Z' || Key == 'z'))
16081  {
16082  AZOrderKeyFlag = true;
16083  }
16084  if(TimeOrderButton->Enabled && (Key == 'Y' || Key == 'y'))
16085  {
16086  TimeOrderKeyFlag = true;
16087  }
16088 /*
16089  if(AddMinsButton->Enabled && (Key == 'M' || Key == 'm')) //can't have key here as adds the letter to the entry
16090  {
16091  AddMinsKeyFlag = true;
16092  }
16093  if(SubMinsButton->Enabled && (Key == 'B' || Key == 'b')) //can't have key here as adds the letter to the entry
16094  {
16095  SubMinsKeyFlag = true;
16096  }
16097 */
16098  if(TTServiceSyntaxCheckButton->Enabled && (Key == 'Q' || Key == 'q'))
16099  {
16101  }
16102  if(ValidateTimetableButton->Enabled && (Key == 'V' || Key == 'v'))
16103  {
16104  ValidateTimetableKeyFlag = true;
16105  }
16106  if(SaveTTButton->Enabled && (Key == 'T' || Key == 't'))
16107  {
16108  SaveTTKeyFlag = true;
16109  }
16110  if(SaveTTAsButton->Enabled && (Key == 'A' || Key == 'a'))
16111  {
16112  SaveTTAsKeyFlag = true;
16113  }
16114  if(RestoreTTButton->Enabled && (Key == 'R' || Key == 'r'))
16115  {
16116  RestoreTTKeyFlag = true;
16117  }
16118  if(ExportTTButton->Enabled && (Key == 'O' || Key == 'o'))
16119  {
16120  ExportTTKeyFlag = true;
16121  }
16122  if(ConflictAnalysisButton->Enabled && (Key == 'F' || Key == 'f'))
16123  {
16124  ConflictAnalysisKeyFlag = true;
16125  }
16126  }
16127  }
16128 // Information menu
16129  if(FloatingInfoMenu->Enabled && !Shift.Contains(ssAlt) && Shift.Contains(ssCtrl) && Shift.Contains(ssShift))
16130  {
16131  if(Key == 'I' || Key == 'i') // toggle track info
16132  {
16133  TrackInfoOnOffMenuItem->Click();
16134  }
16135  if(Key == 'S' || Key == 's') // toggle train status info
16136  {
16138  }
16139  else if(Key == 'T' || Key == 't') // toggle train timetable info
16140  {
16141  TrainTTInfoOnOffMenuItem->Click();
16142  }
16143  else if(Key == 'L' || Key == 'l') // toggle train timetable info
16144  {
16146  }
16147  }
16148 // end of 2.4.2 addition
16149 
16150  }
16151  catch(const Exception &e)
16152  {
16153  ErrorLog(167, e.Message);
16154  }
16155 }
16156 
16157 // ---------------------------------------------------------------------------
16158 
16159 void __fastcall TInterface::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
16160 {
16161  if((Key != VK_SHIFT) && (Key != VK_CONTROL))
16162  {
16163  LastNonCtrlOrShiftKeyDown = -1; // reset value to no key down
16164  }
16165  CtrlKey = false;
16166  ShiftKey = false;
16167  SaveMenuItem->ShortCut = 16467; // restore Ctrl S for save menu in case set to 0 in FormKeyDown
16168 }
16169 
16170 // ---------------------------------------------------------------------------
16171 
16172 void __fastcall TInterface::OutputLog1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16173 {
16174  if((Button == mbRight) && Level2OperMode == Operating)
16175  {
16176  OutputLog1->Caption = "";
16177  }
16178 }
16179 
16180 // ---------------------------------------------------------------------------
16181 
16182 void __fastcall TInterface::OutputLog2MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16183 {
16184  if((Button == mbRight) && Level2OperMode == Operating)
16185  {
16186  OutputLog2->Caption = "";
16187  }
16188 }
16189 // ---------------------------------------------------------------------------
16190 
16191 void __fastcall TInterface::OutputLog3MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16192 {
16193  if((Button == mbRight) && Level2OperMode == Operating)
16194  {
16195  OutputLog3->Caption = "";
16196  }
16197 }
16198 
16199 // ---------------------------------------------------------------------------
16200 
16201 void __fastcall TInterface::OutputLog4MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16202 {
16203  if((Button == mbRight) && Level2OperMode == Operating)
16204  {
16205  OutputLog4->Caption = "";
16206  }
16207 }
16208 
16209 // ---------------------------------------------------------------------------
16210 
16211 void __fastcall TInterface::OutputLog5MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16212 {
16213  if((Button == mbRight) && Level2OperMode == Operating)
16214  {
16215  OutputLog5->Caption = "";
16216  }
16217 }
16218 // ---------------------------------------------------------------------------
16219 
16220 void __fastcall TInterface::OutputLog6MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16221 {
16222  if((Button == mbRight) && Level2OperMode == Operating)
16223  {
16224  OutputLog6->Caption = "";
16225  }
16226 }
16227 
16228 // ---------------------------------------------------------------------------
16229 
16230 void __fastcall TInterface::OutputLog7MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16231 {
16232  if((Button == mbRight) && Level2OperMode == Operating)
16233  {
16234  OutputLog7->Caption = "";
16235  }
16236 }
16237 
16238 // ---------------------------------------------------------------------------
16239 
16240 void __fastcall TInterface::OutputLog8MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16241 {
16242  if((Button == mbRight) && Level2OperMode == Operating)
16243  {
16244  OutputLog8->Caption = "";
16245  }
16246 }
16247 
16248 // ---------------------------------------------------------------------------
16249 
16250 void __fastcall TInterface::OutputLog9MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16251 {
16252  if((Button == mbRight) && Level2OperMode == Operating)
16253  {
16254  OutputLog9->Caption = "";
16255  }
16256 }
16257 
16258 // ---------------------------------------------------------------------------
16259 
16260 void __fastcall TInterface::OutputLog10MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
16261 {
16262  if((Button == mbRight) && Level2OperMode == Operating)
16263  {
16264  OutputLog10->Caption = "";
16265  }
16266 }
16267 
16268 // ---------------------------------------------------------------------------
16269 
16270 void __fastcall TInterface::AboutMenuItemClick(TObject *Sender)
16271 {
16272  try
16273  {
16274  if((Level1Mode == OperMode) && (Level2OperMode != PreStart))
16275  // if PreStart leave as is [Modified at v1.2.0 - formerly just 'if((Level1Mode == OperMode)']
16276  {
16278  SetLevel2OperMode(3);
16279  MasterClock->Enabled = false;
16280  }
16281  AboutForm->ShowModal();
16282  }
16283  catch(const Exception &e)
16284  {
16285  ErrorLog(168, e.Message);
16286  }
16287 }
16288 
16289 // ---------------------------------------------------------------------------
16290 
16291 void __fastcall TInterface::OpenHelpMenuItemClick(TObject *Sender)
16292 {
16293  try
16294  {
16295  // Helpfile allocated during construction of Interface
16296  Application->HelpKeyword(u"Introduction"); // added at v2.0.0 for .chm help file
16297  }
16298  catch(const Exception &e)
16299  {
16300  ErrorLog(175, e.Message);
16301  }
16302 }
16303 
16304 // ---------------------------------------------------------------------------
16305 
16306 void __fastcall TInterface::RailwayWebSiteMenuItemClick(TObject *Sender)
16307 {
16308  const UnicodeString Link = "http://www.railwayoperationsimulator.com";
16309  ::ShellExecute(Handle, NULL, (Link).c_str(), NULL, NULL, SW_SHOWNORMAL);
16310 }
16311 
16312 // ---------------------------------------------------------------------------
16313 
16314 void __fastcall TInterface::BlackBgndMenuItemClick(TObject *Sender)
16315 {
16316  try
16317  {
16318  TrainController->LogEvent("BlackBgndMenuItemClick");
16319  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BlackBgndMenuItemClick");
16320  TColor OldTransparentColour = Utilities->clTransparent;
16321  Utilities->clTransparent = TColor(0);
16322  SelectBitmap->TransparentColor = Utilities->clTransparent;
16325 
16326  MainScreen->Canvas->Brush->Color = Utilities->clTransparent;
16327  MainScreen->Canvas->FillRect(MainScreen->ClientRect);
16328  Level1Mode = BaseMode;
16329  SetLevel1Mode(128);
16330  Utilities->CallLogPop(1797);
16331  }
16332  catch(const Exception &e)
16333  {
16334  ErrorLog(170, e.Message);
16335  }
16336 }
16337 
16338 // ---------------------------------------------------------------------------
16339 
16340 void __fastcall TInterface::WhiteBgndMenuItemClick(TObject *Sender)
16341 {
16342  try
16343  {
16344  TrainController->LogEvent("WhiteBgndMenuItemClick");
16345  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",WhiteBgndMenuItemClick");
16346  TColor OldTransparentColour = Utilities->clTransparent;
16347  Utilities->clTransparent = TColor(0xFFFFFF);
16348  SelectBitmap->TransparentColor = Utilities->clTransparent;
16351 
16352  MainScreen->Canvas->Brush->Color = Utilities->clTransparent;
16353  MainScreen->Canvas->FillRect(MainScreen->ClientRect);
16354  Level1Mode = BaseMode;
16355  SetLevel1Mode(129);
16356  Utilities->CallLogPop(1798);
16357  }
16358  catch(const Exception &e)
16359  {
16360  ErrorLog(171, e.Message);
16361  }
16362 }
16363 
16364 // ---------------------------------------------------------------------------
16365 
16366 void __fastcall TInterface::BlueBgndMenuItemClick(TObject *Sender)
16367 {
16368  try
16369  {
16370  TrainController->LogEvent("BlueBgndMenuItemClick");
16371  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BlueBgndMenuItemClick");
16372  TColor OldTransparentColour = Utilities->clTransparent;
16373  Utilities->clTransparent = TColor(0x330000); //clB1G0R0
16374  SelectBitmap->TransparentColor = Utilities->clTransparent;
16377 
16378  MainScreen->Canvas->Brush->Color = Utilities->clTransparent;
16379  MainScreen->Canvas->FillRect(MainScreen->ClientRect);
16380  Level1Mode = BaseMode;
16381  SetLevel1Mode(130);
16382  Utilities->CallLogPop(1799);
16383  }
16384  catch(const Exception &e)
16385  {
16386  ErrorLog(172, e.Message);
16387  }
16388 }
16389 
16390 // ---------------------------------------------------------------------------
16391 
16392 void __fastcall TInterface::SpeedToggleButtonClick(TObject *Sender)
16393 {
16394  if(SpeedTopLabel->Caption == "mph")
16395  {
16396  SpeedTopLabel->Caption = "km/h";
16397  SpeedBottomLabel->Caption = "mph";
16398  }
16399  else
16400  {
16401  SpeedTopLabel->Caption = "mph";
16402  SpeedBottomLabel->Caption = "km/h";
16403  }
16404  // swap values to match toggle state
16405  UnicodeString SavedTopValue = SpeedEditBox->Text;
16406  UnicodeString SavedBottomValue = SpeedVariableLabel->Caption;
16407 
16408  SpeedEditBox->Text = SavedBottomValue;
16409  SpeedVariableLabel->Caption = SavedTopValue;
16410 }
16411 // ---------------------------------------------------------------------------
16412 
16413 void __fastcall TInterface::SpeedToggleButton2Click(TObject *Sender)
16414 {
16415  if(SpeedTopLabel2->Caption == "mph")
16416  {
16417  SpeedTopLabel2->Caption = "km/h";
16418  SpeedBottomLabel2->Caption = "mph";
16419  }
16420  else
16421  {
16422  SpeedTopLabel2->Caption = "mph";
16423  SpeedBottomLabel2->Caption = "km/h";
16424  }
16425  // swap values to match toggle state
16426  UnicodeString SavedTopValue = SpeedEditBox2->Text;
16427  UnicodeString SavedBottomValue = SpeedVariableLabel2->Caption;
16428 
16429  SpeedEditBox2->Text = SavedBottomValue;
16430  SpeedVariableLabel2->Caption = SavedTopValue;
16431 }
16432 // ---------------------------------------------------------------------------
16433 
16434 void __fastcall TInterface::SpeedEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
16435 {
16436  try
16437  {
16438  TrainController->LogEvent("SpeedEditBoxKeyUp," + AnsiString(Key));
16439  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SpeedEditBoxKeyUp," + AnsiString(Key));
16440  bool ErrorFlag = false, TooBigFlag = false;
16441  if(SpeedEditBox->Text.Length() > 0)
16442  {
16443  if(SpeedEditBox->Text.Length() > 5)
16444  {
16445  TooBigFlag = true;
16446  }
16447  for(int x = 1; x <= SpeedEditBox->Text.Length(); x++)
16448  {
16449  if((SpeedEditBox->Text[x] < '0') || (SpeedEditBox->Text[x] > '9'))
16450  {
16451  SpeedVariableLabel->Caption = "Entry error";
16452  ErrorFlag = true;
16453  break;
16454  }
16455  if(TooBigFlag)
16456  {
16457  SpeedVariableLabel->Caption = "Too big";
16458  break;
16459  }
16460  }
16461  if(!ErrorFlag && !TooBigFlag)
16462  {
16463 /*
16464  1 mph = 1.609344 km/h
16465  1 km/h = 0.621371 mph
16466 */
16467  if(SpeedTopLabel->Caption == "mph")
16468  {
16469  // do mph-to-km/h conversion
16470  int MPH = SpeedEditBox->Text.ToInt();
16471  int KPH = (MPH * 1.609344) + 0.5;
16472  SpeedVariableLabel->Caption = UnicodeString(KPH);
16473  }
16474  else
16475  {
16476  // do km/h-to-mph conversion
16477  int KPH = SpeedEditBox->Text.ToInt();
16478  int MPH = (KPH * 0.621371) + 0.5;
16479  SpeedVariableLabel->Caption = UnicodeString(MPH);
16480  }
16481  }
16482  }
16483  else
16484  {
16485  SpeedVariableLabel->Caption = "";
16486  }
16487  Utilities->CallLogPop(1865);
16488  }
16489  catch(const EConvertError &ec) // thrown for ToInt() conversion error; shouldn't occur but include to prevent a crash //non-error catch
16490  {
16491  SpeedVariableLabel->Caption = "Entry error";
16492  Utilities->CallLogPop(2307);
16493  }
16494  catch(const Exception &e)
16495  {
16496  ErrorLog(176, e.Message);
16497  }
16498 }
16499 
16500 // ---------------------------------------------------------------------------
16501 
16502 void __fastcall TInterface::PowerToggleButtonClick(TObject *Sender)
16503 {
16504  if(PowerTopLabel->Caption == "HP")
16505  {
16506  PowerTopLabel->Caption = "kW";
16507  PowerBottomLabel->Caption = "HP";
16508  }
16509  else
16510  {
16511  PowerTopLabel->Caption = "HP";
16512  PowerBottomLabel->Caption = "kW";
16513  }
16514  // swap values to match toggle state
16515  UnicodeString SavedTopValue = PowerEditBox->Text;
16516  UnicodeString SavedBottomValue = PowerVariableLabel->Caption;
16517 
16518  PowerEditBox->Text = SavedBottomValue;
16519  PowerVariableLabel->Caption = SavedTopValue;
16520 }
16521 // ---------------------------------------------------------------------------
16522 
16523 void __fastcall TInterface::PowerEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
16524 {
16525  try
16526  {
16527  TrainController->LogEvent("PowerEditBoxKeyUp," + AnsiString(Key));
16528  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PowerEditBoxKeyUp," + AnsiString(Key));
16529  bool ErrorFlag = false, TooBigFlag = false;
16530  if(PowerEditBox->Text.Length() > 0)
16531  {
16532  if(PowerEditBox->Text.Length() > 8)
16533  {
16534  TooBigFlag = true;
16535  }
16536  for(int x = 1; x <= PowerEditBox->Text.Length(); x++)
16537  {
16538  if((PowerEditBox->Text[x] < '0') || (PowerEditBox->Text[x] > '9'))
16539  {
16540  PowerVariableLabel->Caption = "Entry error";
16541  ErrorFlag = true;
16542  break;
16543  }
16544  if(TooBigFlag)
16545  {
16546  PowerVariableLabel->Caption = "Too big";
16547  break;
16548  }
16549  }
16550  if(!ErrorFlag && !TooBigFlag)
16551  {
16552 /*
16553  1 kW = 1.340482574 HP
16554  1 HP = 0.745699872 kW
16555 */
16556  if(PowerTopLabel->Caption == "HP")
16557  {
16558  // do HP-to-kW conv
16559  int HP = PowerEditBox->Text.ToInt();
16560  int KW = (HP * 0.745699872) + 0.5;
16561  PowerVariableLabel->Caption = UnicodeString(KW);
16562  }
16563  else
16564  {
16565  // do kW-to-HP conv
16566  int KW = PowerEditBox->Text.ToInt();
16567  int HP = (KW * 1.340482574) + 0.5;
16568  PowerVariableLabel->Caption = UnicodeString(HP);
16569  }
16570  }
16571  }
16572  else
16573  {
16574  PowerVariableLabel->Caption = "";
16575  }
16576  Utilities->CallLogPop(1868);
16577  }
16578  catch(const EConvertError &ec) // thrown for ToInt() conversion error; shouldn't occur but include to prevent a crash //non-error catch
16579  {
16580  PowerVariableLabel->Caption = "Entry error";
16581  Utilities->CallLogPop(2308);
16582  }
16583  catch(const Exception &e)
16584  {
16585  ErrorLog(179, e.Message);
16586  }
16587 }
16588 // ---------------------------------------------------------------------------
16589 
16590 void __fastcall TInterface::SpeedEditBox2KeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
16591 {
16592  try
16593  {
16594  TrainController->LogEvent("SpeedEditBox2KeyUp," + AnsiString(Key));
16595  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SpeedEditBox2KeyUp," + AnsiString(Key));
16596  bool ErrorFlag = false, TooBigFlag = false;
16597  if(SpeedEditBox2->Text.Length() > 0)
16598  {
16599  if(SpeedEditBox2->Text.Length() > 5)
16600  {
16601  TooBigFlag = true;
16602  }
16603  for(int x = 1; x <= SpeedEditBox2->Text.Length(); x++)
16604  {
16605  if((SpeedEditBox2->Text[x] < '0') || (SpeedEditBox2->Text[x] > '9'))
16606  {
16607  SpeedVariableLabel2->Caption = "Entry error";
16608  ErrorFlag = true;
16609  break;
16610  }
16611  if(TooBigFlag)
16612  {
16613  SpeedVariableLabel2->Caption = "Too big";
16614  break;
16615  }
16616  }
16617  if(!ErrorFlag && !TooBigFlag)
16618  {
16619 /*
16620  1 mph = 1.609344 km/h
16621  1 km/h = 0.621371 mph
16622 */
16623  if(SpeedTopLabel2->Caption == "mph")
16624  {
16625  // do mph-to-km/h conversion
16626  int MPH = SpeedEditBox2->Text.ToInt();
16627  int KPH = (MPH * 1.609344) + 0.5;
16628  SpeedVariableLabel2->Caption = AnsiString(KPH);
16629  }
16630  else
16631  {
16632  // do km/h-to-mph conversion
16633  int KPH = SpeedEditBox2->Text.ToInt();
16634  int MPH = (KPH * 0.621371) + 0.5;
16635  SpeedVariableLabel2->Caption = AnsiString(MPH);
16636  }
16637  }
16638  }
16639  else
16640  {
16641  SpeedVariableLabel2->Caption = "";
16642  }
16643  Utilities->CallLogPop(1866);
16644  }
16645  catch(const EConvertError &ec) // thrown for ToInt() conversion error; shouldn't occur but include to prevent a crash //non-error catch
16646  {
16647  SpeedVariableLabel2->Caption = "Entry error";
16648  Utilities->CallLogPop(2309);
16649  }
16650  catch(const Exception &e)
16651  {
16652  ErrorLog(177, e.Message);
16653  }
16654 }
16655 
16656 // ---------------------------------------------------------------------------
16657 
16658 void __fastcall TInterface::LengthEditKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
16659 {
16660  try
16661  {
16662  TrainController->LogEvent("LengthEditKeyUp," + AnsiString(Key));
16663  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LengthEditKeyUp," + AnsiString(Key));
16664  bool ErrorFlag = false, TooLongFlag = false;
16665  if((MileEdit->Text.Length() > 0) && (MileEdit->Text.Length() < 6))
16666  {
16667  for(int x = 1; x <= MileEdit->Text.Length(); x++)
16668  {
16669  if((MileEdit->Text[x] < '0') || (MileEdit->Text[x] > '9'))
16670  {
16671  MetreVariableLabel->Caption = "Entry error";
16672  ErrorFlag = true;
16673  break;
16674  }
16675  }
16676  }
16677  if((ChainEdit->Text.Length() > 0) && (ChainEdit->Text.Length() < 6))
16678  {
16679  for(int x = 1; x <= ChainEdit->Text.Length(); x++)
16680  {
16681  if((ChainEdit->Text[x] < '0') || (ChainEdit->Text[x] > '9'))
16682  {
16683  MetreVariableLabel->Caption = "Entry error";
16684  ErrorFlag = true;
16685  break;
16686  }
16687  }
16688  }
16689  if((YardEdit->Text.Length() > 0) && (YardEdit->Text.Length() < 6))
16690  {
16691  for(int x = 1; x <= YardEdit->Text.Length(); x++)
16692  {
16693  if((YardEdit->Text[x] < '0') || (YardEdit->Text[x] > '9'))
16694  {
16695  MetreVariableLabel->Caption = "Entry error";
16696  ErrorFlag = true;
16697  break;
16698  }
16699  }
16700  }
16701  if((MileEdit->Text.Length() > 5) || (ChainEdit->Text.Length() > 5) || (YardEdit->Text.Length() > 5))
16702  {
16703  TooLongFlag = true;
16704  MetreVariableLabel->Caption = "Too big";
16705  }
16706  if(!ErrorFlag && !TooLongFlag)
16707  {
16708  int Miles = 0, Chains = 0, Yards = 0, Metres = 0;
16709  if(MileEdit->Text.Length() > 0)
16710  {
16711  Miles = MileEdit->Text.ToInt();
16712  }
16713  if(ChainEdit->Text.Length() > 0)
16714  {
16715  Chains = ChainEdit->Text.ToInt();
16716  }
16717  if(YardEdit->Text.Length() > 0)
16718  {
16719  Yards = YardEdit->Text.ToInt();
16720  }
16721  Metres = int((Miles * 1609.344) + (Chains * 20.1168) + (Yards * 0.9144) + 0.5);
16722  MetreVariableLabel->Caption = AnsiString(Metres);
16723  }
16724  if((MileEdit->Text.Length() == 0) && (ChainEdit->Text.Length() == 0) && (YardEdit->Text.Length() == 0))
16725  {
16726  MetreVariableLabel->Caption = "";
16727  }
16728  Utilities->CallLogPop(1867);
16729  }
16730  catch(const EConvertError &ec) // thrown for ToInt() conversion error; shouldn't occur but include to prevent a crash //non-error catch
16731  {
16732  MetreVariableLabel->Caption = "Entry error";
16733  Utilities->CallLogPop(2310);
16734  }
16735  catch(const Exception &e)
16736  {
16737  ErrorLog(178, e.Message);
16738  }
16739 }
16740 
16741 // ---------------------------------------------------------------------------
16742 
16743 void __fastcall TInterface::TTClockAdjButtonClick(TObject *Sender)
16744 {
16745  try
16746  {
16747  TrainController->LogEvent("TTClockAdjButtonClick");
16748  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockAdjButtonClick");
16749 // Utilities->Clock2Stopped = true; // to keep panel buttons disabled, restarted on exit
16750  Display->HideWarningLog(0); // because this panel overwrites it
16751  TTClockAdjPanel->Visible = true;
16752  TTClockAdjButton->Enabled = false;
16753 /*
16754  OperatingPanelLabel->Caption = "Disabled"; all these now dealt with in ClockTimer2
16755  OperatingPanel->Enabled = false;
16756  ZoomButton->Enabled = false;
16757  HomeButton->Enabled = false;
16758  NewHomeButton->Enabled = false;
16759  ScreenLeftButton->Enabled = false;
16760  ScreenRightButton->Enabled = false;
16761  ScreenUpButton->Enabled = false;
16762  ScreenDownButton->Enabled = false;
16763 */
16764  Utilities->CallLogPop(1875);
16765  }
16766  catch(const Exception &e)
16767  {
16768  ErrorLog(181, e.Message);
16769  }
16770 }
16771 
16772 // ---------------------------------------------------------------------------
16773 
16774 void __fastcall TInterface::TTClockExitButtonClick(TObject *Sender)
16775 {
16776  try
16777  {
16778  TrainController->LogEvent("TTClockExitButtonClick");
16779  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockExitButtonClick");
16780  TTClockAdjPanel->Visible = false;
16781  TTClockAdjButton->Enabled = true;
16782 /* these dealt with in ClockTimer2
16783  ZoomButton->Enabled = true;
16784  HomeButton->Enabled = true;
16785  NewHomeButton->Enabled = true;
16786  ScreenLeftButton->Enabled = true;
16787  ScreenRightButton->Enabled = true;
16788  ScreenUpButton->Enabled = true;
16789  ScreenDownButton->Enabled = true;
16790  OperatingPanel->Enabled = true;
16791  OperatingPanelLabel->Caption = "Operation";
16792 */
16793  Display->ShowWarningLog(0);
16794  double TTClockTimeChange = double(TrainController->RestartTime) - PauseEntryRestartTime;
16795  if((TTClockSpeed != PauseEntryTTClockSpeed) || (TTClockTimeChange > 0.000347))
16796  {
16797  // 30 seconds, min increase is 1 minute & don't trust doubles to stay exactly equal
16799  {
16800  TTClockAdjustWarningPanel->Top = MainScreen->Top + ((MainScreen->Height - TTClockAdjustWarningPanel->Height) / 2);
16801  TTClockAdjustWarningPanel->Left = MainScreen->Left + ((MainScreen->Width - TTClockAdjustWarningPanel->Width) / 2);
16802  TTClockAdjustWarningLabel->Caption =
16803  "Changes have been made to the timetable clock - you may wish to save a session before resuming operation.\n\nTo cancel all changes re-click the 'Adjust the timetable clock' button then click the reset button BEFORE resuming operation.";
16804  TTClockAdjustWarningPanel->Visible = true;
16805  }
16806  }
16807 // Utilities->Clock2Stopped = false; // as above
16809 // to restore the ability to reselect T after adj panel hidden (FormKeyUp doesn't work because the Interface form doesn't have focus)
16810  Utilities->CallLogPop(1876);
16811  }
16812  catch(const Exception &e)
16813  {
16814  ErrorLog(182, e.Message);
16815  }
16816 }
16817 // ---------------------------------------------------------------------------
16818 
16819 void __fastcall TInterface::TTClockx2ButtonClick(TObject *Sender)
16820 {
16821  try
16822  {
16823  TrainController->LogEvent("TTClockx2ButtonClick");
16824  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockx2ButtonClick");
16825  TTClockSpeed = 2;
16826  TTClockSpeedLabel->Caption = "x2";
16828  Utilities->CallLogPop(1878);
16829  }
16830  catch(const Exception &e)
16831  {
16832  ErrorLog(184, e.Message);
16833  }
16834 }
16835 
16836 // ---------------------------------------------------------------------------
16837 
16838 void __fastcall TInterface::TTClockx4ButtonClick(TObject *Sender)
16839 {
16840  try
16841  {
16842  TrainController->LogEvent("TTClockx4ButtonClick");
16843  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockx4ButtonClick");
16844  TTClockSpeed = 4;
16845  TTClockSpeedLabel->Caption = "x4";
16847  Utilities->CallLogPop(1883);
16848  }
16849  catch(const Exception &e)
16850  {
16851  ErrorLog(189, e.Message);
16852  }
16853 }
16854 
16855 // ---------------------------------------------------------------------------
16856 
16857 void __fastcall TInterface::TTClockx8ButtonClick(TObject *Sender)
16858 {
16859  try
16860  {
16861  TrainController->LogEvent("TTClockx8ButtonClick");
16862  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockx8ButtonClick");
16863  TTClockSpeed = 8;
16864  TTClockSpeedLabel->Caption = "x8";
16866  Utilities->CallLogPop(1884);
16867  }
16868  catch(const Exception &e)
16869  {
16870  ErrorLog(190, e.Message);
16871  }
16872 }
16873 
16874 // ---------------------------------------------------------------------------
16875 
16876 void __fastcall TInterface::TTClockx16ButtonClick(TObject *Sender)
16877 {
16878  try
16879  {
16880  TrainController->LogEvent("TTClockx16ButtonClick");
16881  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockx16ButtonClick");
16882  TTClockSpeed = 16;
16883  TTClockSpeedLabel->Caption = "x16";
16885  Utilities->CallLogPop(1885);
16886  }
16887  catch(const Exception &e)
16888  {
16889  ErrorLog(191, e.Message);
16890  }
16891 }
16892 
16893 // ---------------------------------------------------------------------------
16894 
16895 void __fastcall TInterface::TTClockx1ButtonClick(TObject *Sender)
16896 {
16897  try
16898  {
16899  TrainController->LogEvent("TTClockx1ButtonClick");
16900  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockx1ButtonClick");
16901  TTClockSpeed = 1;
16902  TTClockSpeedLabel->Caption = "x1";
16904  Utilities->CallLogPop(1886);
16905  }
16906  catch(const Exception &e)
16907  {
16908  ErrorLog(192, e.Message);
16909  }
16910 }
16911 
16912 // ---------------------------------------------------------------------------
16913 
16914 void __fastcall TInterface::TTClockxHalfButtonClick(TObject *Sender)
16915 {
16916  try
16917  {
16918  TrainController->LogEvent("TTClockxHalfButtonClick");
16919  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockxHalfButtonClick");
16920  TTClockSpeed = 0.5;
16921  TTClockSpeedLabel->Caption = "x1/2";
16923  Utilities->CallLogPop(1887);
16924  }
16925  catch(const Exception &e)
16926  {
16927  ErrorLog(193, e.Message);
16928  }
16929 }
16930 
16931 // ---------------------------------------------------------------------------
16932 
16933 void __fastcall TInterface::TTClockxQuarterButtonClick(TObject *Sender)
16934 {
16935  try
16936  {
16937  TrainController->LogEvent("TTClockxQuarterButtonClick");
16938  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockxQuarterButtonClick");
16939  TTClockSpeed = 0.25;
16940  TTClockSpeedLabel->Caption = "x1/4";
16942  Utilities->CallLogPop(1888);
16943  }
16944  catch(const Exception &e)
16945  {
16946  ErrorLog(194, e.Message);
16947  }
16948 }
16949 
16950 // ---------------------------------------------------------------------------
16951 
16952 void __fastcall TInterface::TTClockxEighthButtonClick(TObject *Sender)
16953 {
16954  // added for v2.3.0 for very big railways
16955  try
16956  {
16957  TrainController->LogEvent("TTClockxEighthButtonClick");
16958  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockxEighthButtonClick");
16959  TTClockSpeed = 0.125;
16960  TTClockSpeedLabel->Caption = "x1/8";
16962  Utilities->CallLogPop(2099);
16963  }
16964  catch(const Exception &e)
16965  {
16966  ErrorLog(203, e.Message);
16967  }
16968 }
16969 // ---------------------------------------------------------------------------
16970 
16971 void __fastcall TInterface::TTClockxSixteenthButtonClick(TObject *Sender)
16972 {
16973  // added for v2.3.0 for very big railways
16974  try
16975  {
16976  TrainController->LogEvent("TTClockxSixteenthButtonClick");
16977  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockxSixteenthButtonClick");
16978  TTClockSpeed = 0.0625;
16979  TTClockSpeedLabel->Caption = "x1/16";
16981  Utilities->CallLogPop(2100);
16982  }
16983  catch(const Exception &e)
16984  {
16985  ErrorLog(204, e.Message);
16986  }
16987 }
16988 
16989 // ---------------------------------------------------------------------------
16990 
16991 void __fastcall TInterface::TTClockAdd1hButtonClick(TObject *Sender)
16992 {
16993  try
16994  {
16995  TrainController->LogEvent("TTClockAdd1hButtonClick");
16996  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockAdd1hButtonClick");
16997  double TTClockIncrement = 1.0 / 24;
16998  TrainController->RestartTime += TDateTime(TTClockIncrement);
17001  Utilities->CallLogPop(1879);
17002  }
17003  catch(const Exception &e)
17004  {
17005  ErrorLog(185, e.Message);
17006  }
17007 }
17008 
17009 // ---------------------------------------------------------------------------
17010 
17011 void __fastcall TInterface::TTClockAdd10mButtonClick(TObject *Sender)
17012 {
17013  try
17014  {
17015  TrainController->LogEvent("TTClockAdd10mButtonClick");
17016  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockAdd10mButtonClick");
17017  double TTClockIncrement = 1.0 / 144;
17018  TrainController->RestartTime += TDateTime(TTClockIncrement);
17021  Utilities->CallLogPop(1881);
17022  }
17023  catch(const Exception &e)
17024  {
17025  ErrorLog(187, e.Message);
17026  }
17027 }
17028 
17029 // ---------------------------------------------------------------------------
17030 
17031 void __fastcall TInterface::TTClockAdd1mButtonClick(TObject *Sender)
17032 {
17033  try
17034  {
17035  TrainController->LogEvent("TTClockAdd1mButtonClick");
17036  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockAdd1mButtonClick");
17037  double TTClockIncrement = 1.0 / 1440;
17038  TrainController->RestartTime += TDateTime(TTClockIncrement);
17041  Utilities->CallLogPop(1882);
17042  }
17043  catch(const Exception &e)
17044  {
17045  ErrorLog(188, e.Message);
17046  }
17047 }
17048 
17049 // ---------------------------------------------------------------------------
17050 
17051 void __fastcall TInterface::TTClockResetButtonClick(TObject *Sender)
17052 {
17053  try
17054  {
17055  TrainController->LogEvent("TTClockResetButtonClick");
17056  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockResetButtonClick");
17061  if(TTClockSpeed == 2)
17062  {
17063  TTClockSpeedLabel->Caption = "x2";
17064  }
17065  else if(TTClockSpeed == 4)
17066  {
17067  TTClockSpeedLabel->Caption = "x4";
17068  }
17069  else if(TTClockSpeed == 8)
17070  {
17071  TTClockSpeedLabel->Caption = "x8";
17072  }
17073  else if(TTClockSpeed == 16)
17074  {
17075  TTClockSpeedLabel->Caption = "x16";
17076  }
17077  else if(TTClockSpeed == 0.5)
17078  {
17079  TTClockSpeedLabel->Caption = "x1/2";
17080  }
17081  else if(TTClockSpeed == 0.25)
17082  {
17083  TTClockSpeedLabel->Caption = "x1/4";
17084  }
17085  else if(TTClockSpeed == 0.125)
17086  {
17087  TTClockSpeedLabel->Caption = "x1/8";
17088  }
17089  else if(TTClockSpeed == 0.0625)
17090  {
17091  TTClockSpeedLabel->Caption = "x1/16";
17092  }
17093  else
17094  {
17095  TTClockSpeed = 1;
17096  TTClockSpeedLabel->Caption = "x1";
17097  }
17098  Utilities->CallLogPop(1880);
17099  }
17100  catch(const Exception &e)
17101  {
17102  ErrorLog(186, e.Message);
17103  }
17104 }
17105 
17106 // ---------------------------------------------------------------------------
17107 
17108 void __fastcall TInterface::PresetAutoSigRoutesButtonClick(TObject *Sender)
17109 {
17110  try
17111  {
17112  TrainController->LogEvent("PresetAutoSigRoutesButtonClick");
17113  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PresetAutoSigRoutesButtonClick");
17114  InfoPanel->Caption = "PRE-START: Presetting automatic signal routes";
17115  OperatingPanelLabel->Caption = "Disabled";
17116  OperatingPanel->Enabled = false; // becomes re-enabled during the call to ClockTimer2
17117  ZoomButton->Enabled = false;
17118  HomeButton->Enabled = false;
17119  NewHomeButton->Enabled = false;
17120  ScreenLeftButton->Enabled = false;
17121  ScreenRightButton->Enabled = false;
17122  ScreenUpButton->Enabled = false;
17123  ScreenDownButton->Enabled = false;
17124 
17125  Screen->Cursor = TCursor(-11); // Hourglass
17126  TPrefDirElement StartElement, EndElement;
17127  bool PointsChanged, AtLeastOneSet = false;
17128  int LastIteratorValue = 0;
17129  while(true)
17130  {
17131  if(!EveryPrefDir->GetStartAndEndPrefDirElements(0, StartElement, EndElement, LastIteratorValue))
17132  {
17133  break;
17134  }
17135  // rest of routine here - i.e. build the routes
17136  ConstructRoute->ClearRoute(); // in case not empty though should be
17137  AtLeastOneSet = true;
17138  if(ConstructRoute->GetPreferredRouteStartElement(1, StartElement.HLoc, StartElement.VLoc, EveryPrefDir, true)) // true for AutoSigsFlag
17139  {
17140  }
17141  if(ConstructRoute->GetNextPreferredRouteElement(1, EndElement.HLoc, EndElement.VLoc, EveryPrefDir, true, true, ConstructRoute->ReqPosRouteID,
17142  PointsChanged))
17143  {
17144  }
17146  }
17147  if(AtLeastOneSet)
17148  {
17151  }
17152  else
17153  {
17154  ShowMessage("No presettable automatic signal routes are available");
17155  }
17156  Screen->Cursor = TCursor(-2); // Arrow
17157  Utilities->CallLogPop(1994);
17158  }
17159  catch(const Exception &e)
17160  {
17161  ErrorLog(195, e.Message);
17162  }
17163 }
17164 
17165 // ---------------------------------------------------------------------------
17166 
17167 void __fastcall TInterface::FormResize(TObject *Sender) // new at v2.1.0
17168 {
17169  try
17170  {
17171  if(!SkipFormResizeEvent) // to avoid calling during startup and especially during shutdown
17172  {
17173  // else fails on shutdown because HiddenScreen & other things no longer exist
17174  int DispW = (Interface->Width - 64 - 16) / 16;
17175 // will truncate down to a multiple of 16 (64 = side panels and 16 compensates for excess width of Interface)
17176  int DispH = (Interface->Height - 192) / 16;
17177  MainScreen->Width = DispW * 16;
17178  MainScreen->Height = DispH * 16;
17179  Utilities->ScreenElementWidth = DispW;
17180  Utilities->ScreenElementHeight = DispH;
17181  HiddenScreen->Width = MainScreen->Width;
17182  HiddenScreen->Height = MainScreen->Height;
17183  StaticFeaturesScreen->Width = MainScreen->Width;
17184  StaticFeaturesScreen->Height = MainScreen->Height;
17185 // PerfLogForm->Top = Screen->Height - PerfLogForm->Height - 32; //-32 to avoid overlapping taskbar dropped these after Beta5a so keep last positions
17186 // PerfLogForm->Left = 0;
17187 // ActionsDueForm->Top = Screen->Height -ActionsDueForm->Height - 32; //-32 to avoid overlapping taskbar;;
17188 // ActionsDueForm->Left = Screen->Width - ActionsDueForm->Width;
17189  SigImagePanel->Left = (Interface->Width - SigImagePanel->Width) / 2; // added for v2.3.0
17190  DevelopmentPanel->Width = MainScreen->Width; //added at v2.18.0
17191  DevelopmentPanel->Top = MainScreen->Top + MainScreen->Height - DevelopmentPanel->Height; // new v2.2.0
17192  DevelopmentPanel->Left = MainScreen->Left; // new v2.2.0 amended added at v2.18.0
17193  MTBFEditBox->Left = MainScreen->Left + MainScreen->Width - MTBFEditBox->Width + 32; // new v2.4.0 32 is to place it above the positional panel
17194  MTBFLabel->Left = MainScreen->Left + MainScreen->Width - MTBFEditBox->Width + 30 - 55; // new v2.4.0 placed above and to the left of MTBFEditBox
17195  PositionalPanel->Left = MainScreen->Left + MainScreen->Width; // changed at v2.4.0
17196  PositionalPanel->Top = MainScreen->Top; // changed at v2.4.0
17197  PositionalPanel->Height = MainScreen->Height; // changed at v2.4.0
17198 
17199  if(!Display->ZoomOutFlag)
17200  {
17202  }
17203  else
17204  {
17205  Display->ClearDisplay(11);
17207  }
17208  Display->Update();
17209  }
17210  }
17211  catch(const Exception &e)
17212  {
17213  ErrorLog(197, e.Message);
17214  }
17215 }
17216 
17217 // ---------------------------------------------------------------------------
17218 
17220 {
17221  try
17222  {
17223  TrainController->LogEvent("ConvertToOtherHandSignalsMenuItemClick");
17224  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ConvertToOtherHandSignalsMenuItemClick");
17226  if(Utilities->RHSignalFlag) // RH sigs after conversion
17227  {
17228  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Left Hand Signals";
17230  {
17232  }
17233  else
17234  {
17236  }
17237  SigImagePanel->Caption = "Signals will be on the right hand side of the track";
17238  SigsOnLeftImage1->Visible = false;
17239  SigsOnLeftImage2->Visible = false;
17240  SigsOnRightImage1->Visible = true;
17241  SigsOnRightImage2->Visible = true;
17242  }
17243  else // LH sigs after conversion
17244  {
17245  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Right Hand Signals";
17247  {
17249  }
17250  else
17251  {
17253  }
17254  SigImagePanel->Caption = "Signals will be on the left hand side of the track";
17255  SigsOnRightImage1->Visible = false;
17256  SigsOnRightImage2->Visible = false;
17257  SigsOnLeftImage1->Visible = true;
17258  SigsOnLeftImage2->Visible = true;
17259  }
17260  Utilities->CallLogPop(2097);
17261  }
17262  catch(const Exception &e)
17263  {
17264  ErrorLog(202, e.Message);
17265  }
17266 }
17267 // ---------------------------------------------------------------------------
17268 
17269 void __fastcall TInterface::MTBFEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
17270 
17271 {
17272  try
17273  {
17274  TrainController->LogEvent("MTBFEditBoxKeyUp," + AnsiString(Key));
17275  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MTBFEditBoxKeyUp," + AnsiString(Key));
17276  if((Level1Mode != OperMode) || (Level2OperMode != PreStart))
17277  {
17278  Utilities->CallLogPop(2160);
17279  return;
17280  }
17281  bool TooBigFlag = false, BadCharsFlag = false;
17284  if(MTBFEditBox->Text.Length() > 0)
17285  {
17286  for(int x = 1; x <= MTBFEditBox->Text.Length(); x++)
17287  {
17288  if((MTBFEditBox->Text[x] < '0') || (MTBFEditBox->Text[x] > '9'))
17289  {
17290  BadCharsFlag = true;
17291  break;
17292  }
17293  }
17294  if(!BadCharsFlag)
17295  {
17296  if(StrToInt(MTBFEditBox->Text) > 10000)
17297  {
17298  TooBigFlag = true;
17299  }
17300  }
17301  if(TooBigFlag)
17302  {
17303  ShowMessage("Maximum value allowed is 9999"); //changed from 10,000 at v 2.10.0 but length limited to 4 anyway so 'to big' shouldn't arise
17304  MTBFEditBox->Text = "";
17307  Utilities->CallLogPop(2161);
17308  return;
17309  }
17310  if(BadCharsFlag)
17311  {
17312  ShowMessage("Value must be a whole number with no special characters");
17313  MTBFEditBox->Text = "";
17316  Utilities->CallLogPop(2162);
17317  return;
17318  }
17319  TrainController->AvHoursIntValue = StrToInt(MTBFEditBox->Text); // ok if user enters 0 as that means no failures
17321  }
17323  {
17324  MTBFEditBox->Text = "";
17326  }
17327  Utilities->CallLogPop(2163);
17328  }
17329  catch(const Exception &e)
17330  {
17331  ErrorLog(209, e.Message);
17332  }
17333 }
17334 
17335 // ---------------------------------------------------------------------------
17336 
17337 void __fastcall TInterface::MTBFEditBoxClick(TObject *Sender)
17338 {
17339  try
17340  {
17341  TrainController->LogEvent("MTBFEditBoxClick");
17342  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MTBFEditBoxClick");
17343  if((Level1Mode != OperMode) || (Level2OperMode != PreStart))
17344  {
17345  MTBFEditBox->ReadOnly = true; // it should be anyway but include here for safety
17347  "Values can only be entered or changed in Pre-Start mode\ni.e. after selecting 'Operate railway' but before clicking 'Run'");
17348  }
17349  Utilities->CallLogPop(2164);
17350  }
17351  catch(const Exception &e)
17352  {
17353  ErrorLog(210, e.Message);
17354  }
17355 }
17356 
17357 // ---------------------------------------------------------------------------
17358 
17359 void __fastcall TInterface::UserGraphicButtonClick(TObject *Sender)
17360 {
17361  try
17362  {
17363  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MTBFEditBoxClick");
17364  LengthConversionPanel->Visible = false;
17365  SpeedConversionPanel->Visible = false;
17366  DistanceKey->Visible = false;
17367  ExitHeatmaps();
17369  SetLevel2TrackMode(63);
17370  Display->Update();
17371  if((SelectedGraphicFileName != "") && (!Track->UserGraphicVector.empty()))
17372  // latter condition added at v2.6.0 because showed after ClearAll & reselect failed
17373  {
17374  UserGraphicReselectPanel->Visible = true;
17375  }
17376  else
17377  {
17378  UserGraphicReselectPanel->Visible = false;
17379  LoadUserGraphic(0);
17380  }
17381  Utilities->CallLogPop(2183);
17382  }
17383  catch(const Exception &e)
17384  {
17385  ErrorLog(212, e.Message);
17386  }
17387 }
17388 
17389 // ---------------------------------------------------------------------------
17390 
17391 void __fastcall TInterface::ReselectUserGraphicClick(TObject *Sender)
17392 {
17393  try
17394  {
17395  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ReselectUserGraphicClick");
17396  TrainController->LogEvent("ReselectUserGraphicClick " + SelectedGraphicFileName);
17397  UserGraphicReselectPanel->Visible = false;
17398  TTrack::TUserGraphicMap::iterator UGMIt = Track->UserGraphicMap.find(SelectedGraphicFileName);
17399  if(UGMIt == Track->UserGraphicMap.end())
17400  {
17401  ShowMessage("Unable to find graphic file " + SelectedGraphicFileName + ". Check it still exists.");
17402  Utilities->CallLogPop(2196);
17403  return;
17404  }
17406  SetLevel2TrackMode(64);
17407  Utilities->CallLogPop(2184);
17408  }
17409  catch(const Exception &e)
17410  {
17411  ErrorLog(213, e.Message);
17412  }
17413 }
17414 // ---------------------------------------------------------------------------
17415 
17416 void __fastcall TInterface::SelectNewGraphicClick(TObject *Sender)
17417 {
17418  try
17419  {
17420  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SelectNewGraphicClick");
17421  UserGraphicReselectPanel->Visible = false;
17422  LoadUserGraphic(1);
17423  Utilities->CallLogPop(2185);
17424  }
17425  catch(const Exception &e)
17426  {
17427  ErrorLog(214, e.Message);
17428  }
17429 }
17430 
17431 // ---------------------------------------------------------------------------
17432 
17433 void __fastcall TInterface::TTClockAdjustOKButtonClick(TObject *Sender)
17434 {
17435  try
17436  {
17437  TrainController->LogEvent("TTClockAdjustOKButtonClick");
17438  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TTClockAdjustOKButtonClick");
17439  TTClockAdjustWarningPanel->Visible = false;
17440  if(TTClockAdjustCheckBox->Checked)
17441  {
17442  TTClockAdjustWarningHide = true;
17443  }
17444  Utilities->CallLogPop(2219);
17445  }
17446  catch(const Exception &e)
17447  {
17448  ErrorLog(216, e.Message);
17449  }
17450 }
17451 
17452 // ---------------------------------------------------------------------------
17453 
17454 void __fastcall TInterface::TwoLocationNameButtonClick(TObject *Sender)
17455 {
17456  try
17457  {
17458  TrainController->LogEvent("TwoLocationNameButtonClick");
17459  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TwoLocationNameButtonClick");
17460  TwoLocationNamePanel->Visible = false;
17461  ShowHideTTButton->Enabled = true;
17462  ExitTTModeButton->Enabled = true;
17463  TimetableEditPanel->Enabled = true;
17464  if(TwoLocationNameCheckBox->Checked)
17465  {
17466  TwoLocationNamePanelHide = true;
17467  }
17468  Utilities->CallLogPop(2316);
17469  }
17470  catch(const Exception &e)
17471  {
17472  ErrorLog(224, e.Message);
17473  }
17474 }
17475 
17476 //---------------------------------------------------------------------------
17477 
17478 void __fastcall TInterface::ConflictAnalysisButtonClick(TObject *Sender)
17479 {
17480  try
17481  {
17482  TrainController->LogEvent("ConflictAnalysisButtonClick");
17483  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ConflictAnalysisButtonClick");
17484  ConflictPanel->Visible = true;
17485  Utilities->CallLogPop(2220);
17486  }
17487  catch(const Exception &e)
17488  {
17489  ErrorLog(217, e.Message);
17490  }
17491 }
17492 
17493 // ---------------------------------------------------------------------------
17494 
17495 void __fastcall TInterface::CPCancelButtonClick(TObject *Sender)
17496 {
17497  try
17498  {
17499  TrainController->LogEvent("CPCancelButtonClick");
17500  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CPCancelButtonClick");
17501  ConflictPanel->Visible = false;
17502  Utilities->CallLogPop(2221);
17503  }
17504  catch(const Exception &e)
17505  {
17506  ErrorLog(218, e.Message);
17507  }
17508 }
17509 
17510 // ---------------------------------------------------------------------------
17511 
17512 void __fastcall TInterface::CPGenFileButtonClick(TObject *Sender)
17513 {
17514  try
17515  {
17516  TrainController->LogEvent("CPGenFileButtonClick");
17517  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CPGenFileButtonClick");
17518  if(!CPArrivalsCheckBox->Checked && !CPDeparturesCheckBox->Checked && !CPAtLocCheckBox->Checked && !CPDirectionsCheckBox->Checked)
17519  {
17520  ShowMessage("No boxes ticked!");
17521  }
17522  else // keep ticks & range values from last time, only reset on startup
17523  {
17524  Screen->Cursor = TCursor(-11); // hourglass
17525  AnsiString TTTitle;
17527  {
17528  for(int x = CreateEditTTFileName.Length(); x > 0; x--) // first need to strip out the timetable title from the full name
17529  {
17530  if(CreateEditTTFileName[x] == '\\')
17531  {
17532  TTTitle = CreateEditTTFileName.SubString(x + 1, CreateEditTTFileName.Length() - x - 4);
17533  break;
17534  }
17535  }
17537  CPAtLocCheckBox->Checked, CPDirectionsCheckBox->Checked, CPEditArrRange->Text.ToInt(), CPEditDepRange->Text.ToInt()))
17538  {
17539  ShowMessage("Analysis complete and file created");
17540  }
17541  ConflictPanel->Visible = false;
17542  }
17543  }
17544  Screen->Cursor = TCursor(-2); // arrow
17545  Utilities->CallLogPop(2222);
17546  }
17547  catch(const Exception &e)
17548  {
17549  ErrorLog(219, e.Message);
17550  }
17551 }
17552 
17553 // ---------------------------------------------------------------------------
17554 
17555 void __fastcall TInterface::ReloadConfigMenuItemClick(TObject *Sender) //new for v2.11.0
17556 {
17557  try
17558  {
17559  TrainController->LogEvent("ReloadConfigMenuItemClick");
17560  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ReloadConfigMenuItemClick");
17561  bool NoConfig; //not used
17562  LoadConfigFile(1, false, NoConfig); //false as it's not the first load
17563  Utilities->CallLogPop(2401);
17564  }
17565  catch(const Exception &e)
17566  {
17567  ErrorLog(238, e.Message);
17568  }
17569 }
17570 
17571 // ---------------------------------------------------------------------------
17572 
17573 void TInterface::LoadConfigFile(int Caller, bool FirstLoad, bool &NoConfigFile) //reads each line in isolation, order doesn't matter
17574 {
17575  try
17576  {
17577  //throw Exception(""); to test error message
17578  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadConfigFile," + AnsiString((unsigned char)FirstLoad));
17579  int LengthInt, SpeedInt;
17580  NoConfigFile = false;
17581  std::ifstream ConfigFile((CurDir + "\\Config.txt").c_str()); // added at v2.6.0 to set save & load directories for railways, timetables & session & to
17582  if(ConfigFile.fail()) // no Config file //replace Signal.hnd, Background.col and GNU
17583  {
17584  NoConfigFile = true;
17585  if(FirstLoad) //added atv2.11.0 - may have changed to RH sigs so don't want it resetting to left when load a railway or session
17586  {
17587  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Right Hand Signals";
17588  SigImagePanel->Caption = "Signals will be on the left hand side of the track";
17589  SigsOnLeftImage1->Visible = true;
17590  SigsOnLeftImage2->Visible = true;
17591  SigsOnRightImage1->Visible = false;
17592  SigsOnRightImage2->Visible = false;
17593  ShowMessage(
17594  "This program is free software released under the terms of the GNU General Public License Version 3, as published by the Free Software Foundation. "
17595  " It may be used or redistributed in accordance with that license and is released in the hope that it will be useful, but WITHOUT ANY WARRANTY; "
17596  "without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details - "
17597  "you should have received a copy along with this program but if not see <http://www.gnu.org/licenses/>.");
17598  }
17599  }
17600  else
17601  {
17602  AnsiString ConfigStr = "";
17603  bool ContinueFlag = false;
17604  do
17605  {
17606  if(Utilities->CheckAndReadOneLineFromConfigFile(ConfigFile, ConfigStr))
17607  {
17608  if(ConfigFile.eof())
17609  {
17610  break;
17611  }
17612  //changes at v2.11.0 to add default track lengths and speeds (for user editing) and to add comments
17613  // AnsiString ConfigValue = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17614  if(ConfigStr == "")
17615  {
17616  continue; //ignore blank lines
17617  }
17618  if(ConfigStr[1] == '#')
17619  {
17620  continue; //ignore lines thet begin with ''#'
17621  }
17622  for(int x = 1; x <= ConfigStr.Length(); x++)
17623  {
17624  if(ConfigStr[x] == '#')
17625  {
17626  ConfigStr = ConfigStr.SubString(1, x - 1); //strip the '#' and all after it
17627  //get rid of all spaces & tabs at end of text (Trim() doesn't remove tabs)
17628  while((ConfigStr.SubString(ConfigStr.Length(), 1) == AnsiString(' ')) || (ConfigStr.SubString(ConfigStr.Length(), 1) == AnsiString('\t')))
17629  {
17630  ConfigStr = ConfigStr.SubString(1, ConfigStr.Length() - 1);
17631  }
17632  break;
17633  }
17634  }
17635  AnsiString ConfigValue = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17636  if((ConfigStr.SubString(1, 8) == "Signals=") && FirstLoad)
17637  {
17638  //get rid of all spaces & tabs at beginning of text
17639  while((ConfigValue.SubString(1, 1) == AnsiString(' ')) || (ConfigValue.SubString(1, 1) == AnsiString('\t')))
17640  {
17641  ConfigValue = ConfigValue.SubString(2, ConfigValue.Length() - 1);
17642  }
17643  if((ConfigValue == "right") && !Utilities->RHSignalFlag)
17644  {
17645  RailGraphics->ConvertSignalsToOppositeHand(1); // toggles Utilities->RHSignalFlag in function (sigs always left hand on startup)
17646  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Left Hand Signals";
17648  {
17650  }
17651  else
17652  {
17654  }
17655  SigImagePanel->Caption = "Signals will be on the right hand side of the track";
17656  SigsOnLeftImage1->Visible = false;
17657  SigsOnLeftImage2->Visible = false;
17658  SigsOnRightImage1->Visible = true;
17659  SigsOnRightImage2->Visible = true;
17660  }
17661  else if((ConfigValue == "left") && Utilities->RHSignalFlag)
17662  {
17663  RailGraphics->ConvertSignalsToOppositeHand(2); // toggles Utilities->RHSignalFlag in function
17664  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Right Hand Signals";
17666  {
17668  }
17669  else
17670  {
17672  }
17673  SigImagePanel->Caption = "Signals will be on the left hand side of the track";
17674  SigsOnLeftImage1->Visible = true;
17675  SigsOnLeftImage2->Visible = true;
17676  SigsOnRightImage1->Visible = false;
17677  SigsOnRightImage2->Visible = false;
17678  } //if not either of these then sigs already set as should be
17679  }
17680  if((ConfigStr.SubString(1, 8) == "Heatmap=") && FirstLoad)
17681  {
17682  //get rid of all spaces & tabs at beginning of text
17683  while((ConfigValue.SubString(1, 1) == AnsiString(' ')) || (ConfigValue.SubString(1, 1) == AnsiString('\t')))
17684  {
17685  ConfigValue = ConfigValue.SubString(2, ConfigValue.Length() - 1);
17686  }
17687  if(ConfigValue == "redlow")
17688  {
17691  HeatmapsRedlowvaluesMenuItem->Caption = "Heatmaps: Set Red to Represent High Values";
17692  Utilities->RedLowFlag = true;
17693  }
17694  else if(ConfigValue == "redhigh")
17695  {
17698  HeatmapsRedlowvaluesMenuItem->Caption = "Heatmaps: Set Red to Represent Low Values";
17699  Utilities->RedLowFlag = false;
17700  }
17701  } //if neither then leave as is
17702  if((ConfigStr.SubString(1, 8) == "BgndCol=") && FirstLoad)
17703  {
17704  // pick up transparent colour from file if there is one & set it to the stored value if it's valid else set to black
17705  Utilities->clTransparent = clB0G0R0; // default black background;
17706  if(ConfigValue == "white")
17707  {
17708  Utilities->clTransparent = TColor(0xFFFFFF);
17709  }
17710  else if(ConfigValue == "blue")
17711  {
17712  Utilities->clTransparent = TColor(0x330000);
17713  }
17714  }
17715  if((ConfigStr.SubString(1, 8) == "RLYLocn=") && FirstLoad)
17716  {
17717  if(DirectoryExists(ConfigValue)) // else stays as original directory
17718  {
17719  SaveRailwayDialog->InitialDir = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17720  LoadRailwayDialog->InitialDir = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17721  }
17722  }
17723  else if((ConfigStr.SubString(1, 8) == "TTBLocn=") && FirstLoad)
17724  {
17725  if(DirectoryExists(ConfigValue)) // else stays as original directory
17726  {
17727  TimetableDialog->InitialDir = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17728  SaveTTDialog->InitialDir = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17729  }
17730  }
17731  else if((ConfigStr.SubString(1, 8) == "SSNLocn=") && FirstLoad)
17732  {
17733  if(DirectoryExists(ConfigValue)) // else stays as original directory
17734  {
17735  LoadSessionDialog->InitialDir = ConfigStr.SubString(9, ConfigStr.Length() - 8);
17736  }
17737  }
17738  else if(ConfigStr.SubString(1, 8) == "Length =")
17739  {
17740  for(int x = 1; x <= ConfigValue.Length(); x++)
17741  {
17742  if((ConfigValue[x] < '0') || (ConfigValue[x] > '9'))
17743  {
17744  ContinueFlag = true; //ignore it if invalid
17745  break;
17746  }
17747  }
17748  if(ContinueFlag)
17749  {
17750  ContinueFlag = false;
17751  continue;
17752  }
17753  LengthInt = ConfigStr.SubString(9, ConfigStr.Length() - 8).ToInt();
17754  if(LengthInt < 10)
17755  {
17757  }
17758  if(LengthInt > 99999)
17759  {
17761  }
17762  else
17763  {
17764  Utilities->DefaultTrackLength = LengthInt;
17765  }
17766  }
17767  else if(ConfigStr.SubString(1, 8) == "Speed =")
17768  {
17769  for(int x = 1; x <= ConfigValue.Length(); x++)
17770  {
17771  if((ConfigValue[x] < '0') || (ConfigValue[x] > '9'))
17772  {
17773  ContinueFlag = true; //ignore it if invalid
17774  break;
17775  }
17776  }
17777  if(ContinueFlag)
17778  {
17779  ContinueFlag = false;
17780  continue;
17781  }
17782  SpeedInt = ConfigStr.SubString(9, ConfigStr.Length() - 8).ToInt();
17783  if(SpeedInt < 10)
17784  {
17786  }
17787  else if(SpeedInt > 400)
17788  {
17790  }
17791  else
17792  {
17793  Utilities->DefaultTrackSpeedLimit = SpeedInt;
17794  }
17795  }
17796  }
17797  else
17798  {
17799  break;
17800  }
17801  }
17802  while(!ConfigFile.eof());
17803  ConfigFile.close();
17804  }
17805  Utilities->CallLogPop(2402); //moved here from inside 'else' at v2.13.1
17806  }
17807  catch(const Exception &e)
17808  {
17809  ShowMessage("Unable to load the configuration file, it appears to be corrupt. It will be re-written correctly when the program closes.\n\n"
17810  "Default values will be used for the current session.");
17811  Utilities->RHSignalFlag = false;
17812  ConvertToOtherHandSignalsMenuItem->Caption = "Convert to Right Hand Signals";
17813  SigImagePanel->Caption = "Signals will be on the left hand side of the track";
17814  SigsOnLeftImage1->Visible = true;
17815  SigsOnLeftImage2->Visible = true;
17816  SigsOnRightImage1->Visible = false;
17817  SigsOnRightImage2->Visible = false;
17818  Utilities->clTransparent = clB0G0R0; // default black background;
17819  SaveRailwayDialog->InitialDir = CurDir + "\\" + RAILWAY_DIR_NAME;
17820  LoadRailwayDialog->InitialDir = CurDir + "\\" + RAILWAY_DIR_NAME;
17821  TimetableDialog->InitialDir = CurDir + "\\" + TIMETABLE_DIR_NAME;
17822  SaveTTDialog->InitialDir = CurDir + "\\" + TIMETABLE_DIR_NAME;
17823  LoadSessionDialog->InitialDir = CurDir + "\\" + SESSION_DIR_NAME;
17826  }
17827 }
17828 
17829 //---------------------------------------------------------------------------
17830 
17832 {
17833  try
17834  {
17835  TrainController->LogEvent("SaveConfigFile");
17836  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SaveConfigFile");
17837  // rewrite ConfigFile with signal handedness, background colour & InitialDir values (may be same but no matter)
17838  AnsiString ColourStr = "", SignalStr = "", LengthStr = "", SpeedStr = "", HeatmapColourStr = "";
17839  remove((CurDir + "\\Config.txt").c_str());
17840  std::ofstream ConfigFile((CurDir + "\\Config.txt").c_str());
17841  ColourStr = "black";
17842  SignalStr = "left";
17843  if((Utilities->DefaultTrackLength < 10) || (Utilities->DefaultTrackLength > 99999))
17844  {
17846  }
17848  {
17850  }
17851  LengthStr = AnsiString(Utilities->DefaultTrackLength);
17852  SpeedStr = AnsiString(Utilities->DefaultTrackSpeedLimit);
17853  if(Utilities->clTransparent == TColor(0xFFFFFF))
17854  {
17855  ColourStr = "white";
17856  }
17857  else if(Utilities->clTransparent == TColor(0x330000))
17858  {
17859  ColourStr = "blue";
17860  }
17861  if(Utilities->RHSignalFlag)
17862  {
17863  SignalStr = "right";
17864  }
17865  if(HeatmapsRedlowvaluesMenuItem->Caption == "Heatmaps: Set Red to Represent High Values") // added at v2.22.0
17866  {
17867  HeatmapColourStr = "redlow";
17868  }
17869  else
17870  {
17871  HeatmapColourStr = "redhigh";
17872  }
17873  ConfigFile << AnsiString("#This file contains a list of parameters that are saved after each use of the program and reloaded for the "
17874  "next use. Track element length and speed limit values after the = sign may be changed and the configuration file reloaded "
17875  "during play, but please do not change anything else. Comments begin with '#' and are ignored by the program.\n\n");
17876  ConfigFile << AnsiString("Signals=") << SignalStr << '\n';
17877  ConfigFile << AnsiString("BgndCol=") << ColourStr << '\n';
17878  ConfigFile << AnsiString("RLYLocn=") << AnsiString(LoadRailwayDialog->InitialDir) << '\n';
17879  ConfigFile << AnsiString("TTBLocn=") << AnsiString(TimetableDialog->InitialDir) << '\n';
17880  ConfigFile << AnsiString("SSNLocn=") << AnsiString(LoadSessionDialog->InitialDir) << '\n';
17881  ConfigFile << AnsiString("Heatmap=") << HeatmapColourStr << '\n'; // added at v2.22.0
17882  ConfigFile << AnsiString("Length =") << LengthStr << " #default track element length in metres (not less than 10)\n";
17883  ConfigFile << AnsiString("Speed =") << SpeedStr << " #default track element speed limit in km/h (not less than 10 and not greater than 400)\n";
17884  ConfigFile.close();
17885  Utilities->CallLogPop(2439);
17886  }
17887  catch(const Exception &e)
17888  {
17889  ShowMessage("Unable to save configuration file, file won't be updated");
17890  }
17891 }
17892 
17893 // ---------------------------------------------------------------------------
17894 
17895 void TInterface::SetTopIndex(int Caller)
17896 {
17897 // Set TopIndex to the proper value & also Selected so don't have a different selection to the highlighted entry
17898  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTopIndex");
17899  if((TTCurrentEntryIterator == TimetableEditVector.end()) || TimetableEditVector.empty()) //added at v2.13.0 as a fix for mathstrains19 (discord name) error reported 13/04/22
17900  { //without this it crashes at line before last with "List index out of bounds"
17901  Utilities->CallLogPop(2485);
17902  return;
17903  }
17905  {
17907  }
17908  else if((TTCurrentEntryIterator - TimetableEditVector.begin()) > (AllEntriesTTListBox->TopIndex + 45))
17909  {
17911  }
17912  else //leave AllEntriesTTListBox->TopIndex as it is
17913  {
17914 // AllEntriesTTListBox->TopIndex = AllEntriesTTListBox->TopIndex; //removed at v2.13.0 as serves no purpose
17915  }
17917  Utilities->CallLogPop(2207);
17918 }
17919 
17920 // ---------------------------------------------------------------------------
17921 
17922 void TInterface::ClearandRebuildRailway(int Caller) // now uses HiddenScreen to help avoid flicker
17923 {
17924  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearandRebuildRailway");
17925  bool ClockState = Utilities->Clock2Stopped;
17926 
17927  Utilities->Clock2Stopped = true;
17930  AllRoutes->RebuildRailwayFlag = false; //moved here at v2.14.0 from ClockTimer2 so this function not called twice when called before ClockTimer2 triggered
17931 // Track->RebuildUserGraphics(1, HiddenDisplay); // new at v2.4.0, plot first so all else overwrites, including the grid if selected
17932  if(ScreenGridFlag && (Level1Mode == TrackMode)) //at v2.22.0 moved to RebuildTrackAndText so LongServRefNames plotted before all else
17933  {
17934  int WidthNum = int(MainScreen->Width / 160) + 1;
17935  int HeightNum = int(MainScreen->Height / 144) + 1;
17936  for(int x = 0; x < WidthNum; x++)
17937  {
17938  for(int y = 0; y < HeightNum; y++)
17939  {
17940  HiddenDisplay->PlotAbsolute(0, x * 160, y * 144, RailGraphics->GridBitmap);
17941  }
17942  }
17943  }
17944 // TextHandler->RebuildFromTextVector(1, HiddenDisplay); //This now incorporated in RebuildTrackAndText so that text is plotted after inactive
17945 // elements but before active elements. This is so text can overwite stations and non-station named locations.
17946 
17948 
17949 // Display->Output->Invalidate(); experiment, needs TDisplay Output to be public. Trying to invoke the white flashes that
17950 // used to occur frequently without Disp->Update() in PlotOriginal
17951 
17952  // OperMode LCs plotted below
17954  {
17956  }
17957  if(Level1Mode == PrefDirMode)
17958  {
17959  if(EveryPrefDir->PrefDirSize() > 0)
17960  {
17962  }
17964  {
17966  }
17967  }
17968  if(Level1Mode == TrackMode)
17969  {
17971  {
17972  LocationNameButton->Enabled = true;
17973  }
17974  else
17975  {
17976  LocationNameButton->Enabled = false;
17977  }
17978  }
17980  {
17982  {
17984  DistanceKey->Visible = true;
17985  DistancesMarked = true;
17986  }
17987  LengthConversionPanel->Visible = true;
17988  SpeedConversionPanel->Visible = true;
17989  }
17990  if(Level2TrackMode == DistanceContinuing) // for extended distances
17991  {
17992  if(ConstructPrefDir->PrefDirSize() > 0)
17993  {
17994 // this line was after the next line until v2.5.1, changed so magenta not overrridden after PrefDirMarker called
17996  LengthConversionPanel->Visible = true;
17997  SpeedConversionPanel->Visible = true;
17999  LengthHeatMapImage->Visible = false;
18000  SpeedHeatMapImage->Visible = false;
18001  DistanceKey->Visible = true;
18002  DistancesMarked = true;
18003  }
18004  }
18006  // this is to keep the distance markers if they are already present when Select is chosen, in case user wishes to choose SelectLengths,
18007  // don't need to display ConstructPrefDir marker as that only needed in DistanceContinuing mode
18008  {
18009  ExitHeatmaps();
18011  DistanceKey->Visible = true;
18012  }
18014  // cancel DistancesMarked if exit from any of these modes
18015  {
18016  DistancesMarked = false;
18017  DistanceKey->Visible = false;
18018  ExitHeatmaps();
18019  LengthConversionPanel->Visible = false; // added at v1.3.1 to remove when distance/speed setting exited
18020  SpeedConversionPanel->Visible = false; // added at v1.3.1 to remove when distance/speed setting exited
18021  }
18023  // in process of moving so use NewSelectBitmapHLoc & VLoc
18024  {
18026  }
18027 
18029  // not in process of moving or failed to click mouse within selection so use SelectBitmapHLoc & VLoc
18030  {
18032  }
18033 
18034  if(Level1Mode == OperMode)
18035  {
18037  if(!AllRoutes->LockedRouteVector.empty())
18038  {
18039  for(TAllRoutes::TLockedRouteVectorIterator LRVIT = AllRoutes->LockedRouteVector.end() - 1; LRVIT >= AllRoutes->LockedRouteVector.begin(); LRVIT--)
18040  {
18041  if(!(AllRoutes->TrackIsInARoute(7, LRVIT->LastTrackVectorPosition, LRVIT->LastXLinkPos)))
18042  {
18043  AllRoutes->LockedRouteVector.erase(LRVIT);
18044  // if end element not in route then a train must have entered it from the wrong end and erased the whole route,
18045  // hence no longer needed so get rid of it (end of route can't be points, crossover or bridge so danger of
18046  // route being on the other track of a 2-track element doesn't arise)
18047  continue;
18048  }
18049  TOneRoute Route = AllRoutes->GetFixedRouteAt(0, LRVIT->RouteNumber);
18050 // int x = Route.PrefDirSize() - 1;
18051 //here need to find the PrefDirVector position for Route that corresponds to LRVIT->LastTrackVectorPosition
18052  int FrontPDPos = -1; //added at v2.15.0 for front truncation
18053  for(int x = (Route.PrefDirSize() - 1); x >= 0; x--)
18054  {
18055  if(Route.GetFixedPrefDirElementAt(262, x).GetTrackVectorPosition() == LRVIT->LastTrackVectorPosition)
18056  {
18057  FrontPDPos = x;
18058  }
18059  }
18060  if(FrontPDPos == -1)
18061  {
18062  throw Exception("Failed to find LastTrackVectorPosition in Clearand... for a locked route");
18063  }
18064  bool BreakFlag = false;
18065  TPrefDirElement PrefDirElement = Route.GetFixedPrefDirElementAt(1, FrontPDPos);
18066  while(PrefDirElement.GetTrackVectorPosition() != LRVIT->RearTrackVectorPosition)
18067  {
18068  HiddenDisplay->PlotOutput(10, (PrefDirElement.HLoc) * 16, (PrefDirElement.VLoc) * 16,
18069  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
18070  if(!(AllRoutes->TrackIsInARoute(8, PrefDirElement.Conn[PrefDirElement.GetELinkPos()],
18071  PrefDirElement.ConnLinkPos[PrefDirElement.GetELinkPos()])))
18072  {
18073  BreakFlag = true;
18074  break; // train removed earlier element from route so stop here
18075  }
18076  FrontPDPos--;
18077  if(FrontPDPos < 0) // added after Albie Vowles reported error on 14/08/20 by email
18078  {
18079  // it means that part of the route (including that at the truncate point) has been cancelled, in this case by a train running past the signal
18080  BreakFlag = true;
18081 // at danger and cancelling the route elements in front of it. The locked route is now too short and this 'while' loop won't find
18082  break; // it, so x keeps decrementing and when it becomes -1 an error is thrown. This addition prevents the error.
18083  }
18084  PrefDirElement = Route.GetFixedPrefDirElementAt(2, FrontPDPos);
18085  }
18086  if(!BreakFlag)
18087  {
18088  if(PrefDirElement.GetTrackVectorPosition() == LRVIT->RearTrackVectorPosition)
18089  {
18090  HiddenDisplay->PlotOutput(11, (PrefDirElement.HLoc) * 16, (PrefDirElement.VLoc) * 16,
18091  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
18092  }
18093  }
18094  }
18095  }
18096  if(RouteMode == RouteContinuing)
18097  {
18099 // system thinks overlay is already plotted, so plot original to reset the OverlayPlotted flag
18102  if(AutoSigsFlag)
18103  {
18105  }
18106  else if(PreferredRoute) // added at v2.7.0, was ConsecSignalsRoute
18107  {
18109  }
18110  else
18111  {
18113  }
18114  }
18115  if(Track->PointFlashFlag)
18116  {
18117  // need to reset the screen location for picking up the original graphic
18118  int Left, Top; // Embarcadero change - these missing in error from Borland file
18120  // note that the above Pos values are wrt layout, not the screen, but the Left & Top values are wrt screen
18121  PointFlash->SetSourceRect(Left, Top);
18122  PointFlash->LoadOriginalScreenGraphic(4); // reload from new position
18123  // doesn't matter whether Flash was on or off when this function called as will sort itself out later (may miss a flash but won't be noticeable)
18124  }
18125  // now plot level crossings (must be after routes). These don't need any base elements to be plotted as they are already plotted.
18126  // In order to avoid plotting the whole LC for every element of a LC a bool value - LCPlotted - is used to save time
18127  for(unsigned int x = 0; x < Track->LCVector.size(); x++)
18128  {
18129  (Track->InactiveTrackVector.begin() + (*(Track->LCVector.begin() + x)))->LCPlotted = false;
18130  }
18131  for(unsigned int x = 0; x < Track->LCVector.size(); x++)
18132  {
18133  int BaseSpeedTag;
18134  TTrackElement ATE;
18135  TTrackElement ITE = *(Track->InactiveTrackVector.begin() + (*(Track->LCVector.begin() + x)));
18136  {
18137  BaseSpeedTag = Track->GetTrackElementFromTrackMap(0, ITE.HLoc, ITE.VLoc).SpeedTag;
18138  if(ITE.LCPlotted == false)
18139  {
18140  if(ITE.Attribute == 0)
18141  {
18143  }
18144  else if(ITE.Attribute == 1)
18145  {
18146  // need to determine if should plot green (manual) or red (auto), but all linked LCs have ConsecSignals set to 2 in BarriersDownVector if manual
18147  // so just need to test this for the HLoc & VLoc position match
18148  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++)
18149  {
18150  if((Track->BarriersDownVector.at(x).HLoc == ITE.HLoc) && (Track->BarriersDownVector.at(x).VLoc == ITE.VLoc))
18151  {
18152  if(Track->BarriersDownVector.at(x).TypeOfRoute == 2)
18153  {
18155  true); // true for manual = green
18156  }
18157  else
18158  {
18160  false); // false for auto = red
18161  }
18162  }
18163  }
18164  }
18165  // if ITE->Attribute == 2 then LC is changing, FlashingGraphics will take care of flashing & final plotting,
18166  // it won't set LCPlotted but no real time lost in this case
18167  }
18168  }
18169  }
18170  //populate StaticFeaturesDisplay at this point prior to train plotting
18171  StaticFeaturesDisplay->GetImage()->Picture->Bitmap->Assign(HiddenScreen->Picture->Bitmap); //now has same offsets as mainscreen
18173  }
18174  Display->ZoomOutFlag = false;
18175  ZoomButton->Glyph->LoadFromResourceName(0, "ZoomOut");
18176  MainScreen->Picture->Bitmap->Assign(HiddenScreen->Picture->Bitmap);
18177  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
18178  Utilities->Clock2Stopped = ClockState;
18179  Utilities->CallLogPop(91);
18180 }
18181 
18182 // ---------------------------------------------------------------------------
18183 
18184 bool TInterface::HighLightOneGap(int Caller, int &HLoc, int &VLoc)
18185 {
18186  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",HighLightOneGap");
18187  if(Track->FindAndHighlightAnUnsetGap(1)) // true if find one
18188  {
18189  if(!PreventGapOffsetResetting) // don't reset display position if returning from zoomout mode
18190  {
18191  while((Display->DisplayOffsetH - Track->GetGapHLoc()) > 0)
18192  {
18193  Display->DisplayOffsetH -= (Utilities->ScreenElementWidth / 2); // use 30 instead of 60 so less likely to appear behind the message box
18194  }
18196  {
18198  }
18199  while((Display->DisplayOffsetV - Track->GetGapVLoc()) > 0)
18200  {
18201  Display->DisplayOffsetV -= (Utilities->ScreenElementHeight / 2); // use 18 instead of 36 so less likely to appear behind the message box
18202  }
18204  {
18206  }
18207  }
18208  InfoPanel->Visible = true;
18209  InfoPanel->Caption = "CONNECTING GAPS: Click on connecting element";
18210  ClearandRebuildRailway(31); // get rid of earlier gap selection
18211  Utilities->CallLogPop(92);
18212  return(true); // return as one now identified & over to MainScreenMouseDown with Level2TrackMode = GapSetting
18213  }
18214  Utilities->CallLogPop(93);
18215  return(false); // no unset ones left to find
18216 }
18217 
18218 // ---------------------------------------------------------------------------
18219 
18221 {
18222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearEverything");
18223  if(FileChangedFlag)
18224  {
18225  UnicodeString MessageStr = "The railway has changed, close it without saving?";
18226  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
18227  if(button == IDNO)
18228  {
18229  Utilities->CallLogPop(1140);
18230  return(false);
18231  }
18232  }
18233  Display->ClearDisplay(7);
18235 
18236  Display->DisplayOffsetH = 0;
18237  Display->DisplayOffsetV = 0;
18242 
18243 // these ensure that all persistent vectors, maps & multimaps etc are cleared
18244  delete TrainController;
18245  delete EveryPrefDir;
18246  delete SelectPrefDir;
18247  delete ConstructRoute;
18248  delete ConstructPrefDir;
18249  delete AllRoutes;
18250  delete Track;
18251  delete TextHandler;
18252 // NB can't delete & recreate Utilities or will lose the CallLog file & have errors due to log being empty when try to
18253 // pop earlier pushed values
18254 // OK though as no containers in Utilities that need to clear & PerformanceFile recreated when begin to operate a later
18255 // railway
18256  TextHandler = new TTextHandler;
18257  Track = new TTrack;
18258  AllRoutes = new TAllRoutes;
18260  ConstructRoute = new TOneRoute;
18261  EveryPrefDir = new TOnePrefDir;
18262  SelectPrefDir = new TOnePrefDir;
18264  PerfLogForm->PerformanceLogBox->Lines->Clear();
18265  ResetAll(1);
18266  bool NoConfig; //not used
18267  LoadConfigFile(2, false, NoConfig); //reset default track element length & speed limit (uninitialised when Track recreated), false as it's not the first load
18268 
18269  Utilities->CallLogPop(94);
18270  return(true);
18271 }
18272 
18273 // ---------------------------------------------------------------------------
18274 
18275 bool TInterface::FileIntegrityCheck(int Caller, char *FileName) const // true for success
18276 {
18277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FileIntegrityCheck," + AnsiString(FileName));
18278  std::ifstream VecFile(FileName);
18279 
18280  if(VecFile.is_open())
18281  {
18282  if(!Utilities->CheckFileStringZeroDelimiter(VecFile)) // Program version
18283  {
18284  VecFile.close();
18285  Utilities->CallLogPop(1805);
18286  return(false);
18287  }
18288  if(!Utilities->CheckFileInt(VecFile, -1000000, 1000000)) // DisplayOffsetHHome
18289  {
18290  VecFile.close();
18291  Utilities->CallLogPop(1440);
18292  return(false);
18293  }
18294  if(!Utilities->CheckFileInt(VecFile, -1000000, 1000000)) // DisplayOffsetVHome
18295  {
18296  VecFile.close();
18297  Utilities->CallLogPop(1441);
18298  return(false);
18299  }
18300  bool GraphicsFollow = false;
18301  int NumberOfActiveElements;
18302  if(!(Track->CheckTrackElementsInFile(1, NumberOfActiveElements, GraphicsFollow, VecFile))) // for new loads
18303  {
18304  VecFile.close();
18305  Utilities->CallLogPop(95);
18306  return(false);
18307  }
18308  if(!(TextHandler->CheckTextElementsInFile(0, VecFile)))
18309  {
18310  VecFile.close();
18311  Utilities->CallLogPop(96);
18312  return(false);
18313  }
18314  if(!(EveryPrefDir->CheckOnePrefDir(0, NumberOfActiveElements, VecFile)))
18315  {
18316  VecFile.close();
18317  Utilities->CallLogPop(97);
18318  return(false);
18319  }
18320  if(GraphicsFollow)
18321  {
18322  if(!Track->CheckUserGraphics(0, VecFile, CurDir + "\\" + USERGRAPHICS_DIR_NAME)) // include path to Graphics folder
18323  {
18324  VecFile.close();
18325  Utilities->CallLogPop(2186);
18326  return(false);
18327  }
18328  }
18329  VecFile.close();
18330  }
18331  else
18332  {
18333  Utilities->CallLogPop(1153);
18334  return(false);
18335  }
18336  Utilities->CallLogPop(98);
18337  return(true);
18338 }
18339 
18340 // ---------------------------------------------------------------------------
18341 
18342 void TInterface::Delay(int Caller, double Msec)
18343 {
18344  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Delay," + AnsiString(Msec));
18345  TDateTime First, Second;
18346  bool Finished = false;
18347 
18348  First = TDateTime::CurrentDateTime();
18349  double TimeVal1 = 86400000 * double(First); // no of msec in a day
18350 
18351  while(!Finished)
18352  {
18353  Second = TDateTime::CurrentDateTime();
18354  double TimeVal2 = 86400000 * double(Second);
18355  if((TimeVal2 - TimeVal1) > Msec)
18356  {
18357  Finished = true;
18358  }
18359  }
18360  Utilities->CallLogPop(1203);
18361 }
18362 
18363 // ---------------------------------------------------------------------------
18364 
18366 {
18367  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetCurrentSpeedButton");
18368  if(CurrentSpeedButton)
18369  {
18370  CurrentSpeedButton->Down = false;
18371  }
18372  CurrentSpeedButton = 0;
18373  Utilities->CallLogPop(1204);
18374 }
18375 
18376 // ---------------------------------------------------------------------------
18377 
18379 {
18380  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",MovingTrainPresentOnFlashingRoute");
18381  int TrainID;
18382 
18383  if(ConstructRoute->SearchVectorSize() == 0)
18384  {
18385  Utilities->CallLogPop(99);
18386  return(false);
18387  }
18388  for(unsigned int x = 0; x < ConstructRoute->SearchVectorSize(); x++)
18389  {
18390  TPrefDirElement PrefDirElement = ConstructRoute->GetFixedSearchElementAt(14, x);
18391  if(PrefDirElement.TrackType == Bridge)
18392  {
18393  if(PrefDirElement.GetXLinkPos() < 2)
18394  {
18396  }
18397  else
18398  {
18400  }
18401  }
18402  else
18403  {
18404  TrainID = Track->TrackElementAt(488, PrefDirElement.GetTrackVectorPosition()).TrainIDOnElement;
18405  }
18406  if((TrainID > -1) && !(TrainController->TrainVectorAtIdent(4, TrainID).Stopped()))
18407  {
18408  Utilities->CallLogPop(100);
18409  return(true);
18410  }
18411  // check for crossed diagonal fouling by train added at v1.2.0
18412  int TrainID; // not used
18413  int LinkNumber1 = PrefDirElement.Link[PrefDirElement.GetELinkPos()];
18414  int LinkNumber2 = PrefDirElement.Link[PrefDirElement.GetXLinkPos()];
18415  if((LinkNumber1 == 1) || (LinkNumber1 == 3) || (LinkNumber1 == 7) || (LinkNumber1 == 9))
18416  {
18417  if(Track->DiagonalFouledByTrain(1, PrefDirElement.HLoc, PrefDirElement.VLoc, LinkNumber1, TrainID))
18418  {
18419  Utilities->CallLogPop(2037);
18420  return(true);
18421  }
18422  }
18423  if((LinkNumber2 == 1) || (LinkNumber2 == 3) || (LinkNumber2 == 7) || (LinkNumber2 == 9))
18424  {
18425  if(Track->DiagonalFouledByTrain(2, PrefDirElement.HLoc, PrefDirElement.VLoc, LinkNumber2, TrainID))
18426  {
18427  Utilities->CallLogPop(2038);
18428  return(true);
18429  }
18430  }
18431  }
18432  Utilities->CallLogPop(101);
18433  return(false);
18434 }
18435 
18436 // ---------------------------------------------------------------------------
18437 
18439 {
18440  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RevertToOriginalRouteSelector");
18441  AutoRouteStartMarker->PlotOriginal(26, Display); // if overlay not plotted will ignore
18442  SigRouteStartMarker->PlotOriginal(27, Display); // if overlay not plotted will ignore
18443  NonSigRouteStartMarker->PlotOriginal(28, Display); // if overlay not plotted will ignore
18444  RouteCancelFlag = false;
18446  {
18447  RouteCancelButton->Enabled = true;
18448  }
18449  else
18450  {
18451  RouteCancelButton->Enabled = false;
18452  }
18455 // reset here so that a n element that has been selected and then not doesn't remain set as a single element
18456  InfoPanel->Visible = true;
18457  if(Level2OperMode != Paused)
18458  {
18459  InfoPanel->Caption = InfoCaptionStore;
18460  }
18461  Utilities->CallLogPop(102);
18462 }
18463 
18464 // ---------------------------------------------------------------------------
18465 
18466 // usermode functions below
18468 {
18469  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetLevel1Mode");
18470  if(!Display->ZoomOutFlag)
18471  {
18474  Track->GapFlashFlag = false;
18475  }
18476 // GapFlash resets when any mode selected unless zoomed out
18477 // note that if selecting zoom back in then this will be called before ZoomOutFlag is reset so won't
18478 // reset GapFlashFlag
18479  switch(Level1Mode) // use the data member
18480  {
18481  case BaseMode:
18482  CopyMenuItem->ShortCut = TextToShortCut(""); // added these for v2.1.0 to set default values after use of the 'Edit' menu during track building
18483  CutMenuItem->ShortCut = TextToShortCut(""); // to allow normal cutting/copying/pasting, especially in timetable construction or editing
18484  PasteMenuItem->ShortCut = TextToShortCut("");
18489  LengthConversionPanel->Visible = false;
18490  SpeedConversionPanel->Visible = false;
18491  TimetableEditPanel->Visible = false;
18492  TrainController->TTEditPanelVisible = false; // added at v2.6.0 for two location message
18493  TrackBuildPanel->Visible = false;
18494  TrackElementPanel->Visible = false;
18495  LocationNameTextBox->Visible = false;
18496  TextBox->Visible = false;
18497  TrackLengthPanel->Visible = false;
18498  InfoPanel->Visible = false;
18499  PrefDirPanel->Visible = false;
18500  TimetablePanel->Visible = false;
18501  OperatingPanel->Visible = false;
18502  PrefDirKey->Visible = false;
18503  TrackLinkedImage->Visible = false;
18504  TrackNotLinkedImage->Visible = false;
18505  GapsSetImage->Visible = false;
18506  GapsNotSetImage->Visible = false;
18507  LocationNamesSetImage->Visible = false;
18508  LocationNamesNotSetImage->Visible = false;
18509  ModeMenu->Enabled = true;
18510  FileMenu->Enabled = true;
18511  EditMenu->Enabled = false;
18512  BuildTrackMenuItem->Enabled = true;
18513  MultiplayerMenu->Enabled = false;
18514  CouplingFileLoadedFlag = false;
18516  PlayerReadyToBeginFlag = false;
18517  PlayerCancelJoinFlag = false;
18519  HostInSessionFlag = false;
18520  PlayerInSessionFlag = false;
18521  SigAspectButton->Enabled = false;
18522  Track->ChangingLCVector.clear();
18523  Track->BarriersDownVector.clear();
18525  ConvertToOtherHandSignalsMenuItem->Enabled = false; // new at v2.3.0
18526  SigImagePanel->Visible = false; // new at v2.3.0
18527  MTBFEditBox->Visible = false; // new at v2.4.0
18528  MTBFLabel->Visible = false;
18529  TTClockAdjustWarningPanel->Visible = false;
18531  DelayMenu->Visible = false; //added at v2.13.0
18532  DelayMenu->Enabled = false;
18533  FailureMenu->Visible = false; //added at v2.14.0
18534  FailureMenu->Enabled = false;
18535  FlashControlButton->Glyph->LoadFromResourceName(0, "NoFlash"); //added at v2.18.0 so reloads with normal flashing
18536  FlashControlButton->Visible = false; //added at v2.15.0
18537  StopFlashFlag = false;
18538  if(Track->IsTrackFinished())
18539  {
18540  PlanPrefDirsMenuItem->Enabled = true;
18541  if(TimetableTitle != "")
18542  {
18543  OperateRailwayMenuItem->Enabled = true;
18544  }
18545  else
18546  {
18547  OperateRailwayMenuItem->Enabled = false;
18548  }
18549  }
18550  else
18551  {
18552  PlanPrefDirsMenuItem->Enabled = false;
18553  OperateRailwayMenuItem->Enabled = false;
18554  }
18555  if(RlyFile)
18556  {
18557  LoadTimetableMenuItem->Enabled = true;
18558  }
18559  else
18560  {
18561  LoadTimetableMenuItem->Enabled = false;
18562  }
18563  LoadRailwayMenuItem->Enabled = true;
18564  if(NoRailway())
18565  {
18566  SaveAsMenuItem->Enabled = false;
18567  ImageMenu->Enabled = false;
18568  SaveImageAndGridMenuItem->Enabled = false;
18569  SaveImageNoGridMenuItem->Enabled = false;
18570  SaveImageAndPrefDirsMenuItem->Enabled = false;
18571  SaveOperatingImageMenuItem->Enabled = false;
18572  BlackBgndMenuItem->Enabled = false;
18573  WhiteBgndMenuItem->Enabled = false;
18574  BlueBgndMenuItem->Enabled = false;
18575  HeatmapsRedlowvaluesMenuItem->Enabled = true;
18576  ConvertToOtherHandSignalsMenuItem->Enabled = true; // new at v2.3.0
18577  SigImagePanel->Visible = true; // new at v2.3.0
18578  if(Utilities->clTransparent != TColor(0))
18579  {
18580  BlackBgndMenuItem->Enabled = true;
18581  }
18582  if(Utilities->clTransparent != TColor(0xFFFFFF))
18583  {
18584  WhiteBgndMenuItem->Enabled = true;
18585  }
18586  if(Utilities->clTransparent != TColor(0x330000))
18587  {
18588  BlueBgndMenuItem->Enabled = true;
18589  }
18590  ClearAllMenuItem->Enabled = false;
18591  InfoPanel->Visible = true;
18592  InfoPanel->Caption = "Select an option from the File, Mode or Help menus";
18593  }
18594  else
18595  {
18596  InfoPanel->Visible = false;
18597  SaveAsMenuItem->Enabled = true;
18598  ImageMenu->Enabled = true;
18599  SaveImageAndGridMenuItem->Enabled = true;
18600  SaveImageNoGridMenuItem->Enabled = true;
18601  if(EveryPrefDir->PrefDirSize() > 0)
18602  {
18603  SaveImageAndPrefDirsMenuItem->Enabled = true;
18604  }
18605  else
18606  {
18607  SaveImageAndPrefDirsMenuItem->Enabled = false;
18608  }
18609  BlackBgndMenuItem->Enabled = false;
18610  WhiteBgndMenuItem->Enabled = false;
18611  BlueBgndMenuItem->Enabled = false;
18612  HeatmapsRedlowvaluesMenuItem->Enabled = false;
18613  SaveOperatingImageMenuItem->Enabled = false;
18614  ClearAllMenuItem->Enabled = true;
18615  }
18616  if(SavedFileName == "")
18617  {
18618  SaveMenuItem->Enabled = false;
18619  }
18620  else if(!FileChangedFlag)
18621  {
18622  SaveMenuItem->Enabled = false;
18623  }
18624  else if((SavedFileName[SavedFileName.Length()] == 'y') || (SavedFileName[SavedFileName.Length()] == 'Y')) // 'rly' file
18625  {
18626  if(!(Track->IsReadyForOperation(false)))
18627  {
18628  SaveMenuItem->Enabled = false; // can't save under its old name as not now a .rly file
18629  }
18630  else
18631  {
18632  SaveMenuItem->Enabled = true; // must have changed some of the PrefDirs (because FileChangedFlag is true)
18633  }
18634  }
18635  else
18636  {
18637  SaveMenuItem->Enabled = true;
18638  }
18639  LoadSessionMenuItem->Enabled = true;
18640  ExitMenuItem->Enabled = true;
18641  ScreenGridFlag = false;
18642  TrainController->CrashWarning = false;
18643  TrainController->DerailWarning = false;
18644  TrainController->SPADWarning = false;
18646  TrainController->CallOnWarning = false;
18649  UserGraphicReselectPanel->Visible = false;
18650  ClearandRebuildRailway(32); // to get rid of unwanted displays (eg distance markers)
18651  SetTrackBuildImages(13);
18652  ClipboardChecked = false;
18653  Utilities->CumulativeDelayedRandMinsAllTrains = 0; //added at v2.13.0
18654 // Utilities->LastTSRCheckTime = TDateTime(0); //added at v2.13.0 //removed at v2.14.0, now value set to TTClockTime when start operation or load session
18656 // Utilities->LastTSRCheckTime = TDateTime(0); removed at v2.13.2 as redundant (covered above)
18657  Track->FailedPointsVector.clear();
18658  Track->FailedSignalsVector.clear();
18659  Track->TSRVector.clear();
18660  Track->SimpleVector.clear();
18661  break;
18662 
18663  case TimetableMode:
18664  if(TwoLocationNamePanel->Visible) //added at v2.9.1 so panel persists until button clicked
18665  {
18666  break;
18667  }
18671  ModeMenu->Enabled = false;
18672  SigImagePanel->Visible = false; // new at v2.3.0
18673  FileMenu->Enabled = false;
18674  EditMenu->Enabled = false;
18675  FloatingInfoMenu->Enabled = false;
18676  ImageMenu->Enabled = false;
18677  TimetableEditPanel->BringToFront();
18678  TimetableHandler();
18679  break;
18680 
18681  case TrackMode:
18682  {
18683  if(Level2TrackMode == CutMoving)
18684  {
18685  Level2TrackMode = Pasting; // paste the selection
18686  SetLevel2TrackMode(52); // CancelSelectionFlag used in Case Pasting
18687  }
18692  TrackBuildPanel->Visible = true;
18693  TrackBuildPanelLabel->Caption = "Build/modify";
18694  TrackElementPanel->Visible = false;
18695  TrackLengthPanel->Visible = false;
18696  PrefDirPanel->Visible = false;
18697  TimetablePanel->Visible = false;
18698  OperatingPanel->Visible = false;
18699  InfoPanel->Visible = false;
18700  InfoPanel->Caption = "";
18701  LocationNameTextBox->Visible = false;
18702  TextBox->Visible = false;
18703  ModeMenu->Enabled = false;
18704  SigImagePanel->Visible = false; // new at v2.3.0
18705  FileMenu->Enabled = false;
18706  // set edit menu items
18708  // display track buttons
18709  AddTrackButton->Enabled = true;
18711  {
18712  LocationNameButton->Enabled = true;
18713  }
18714  else
18715  {
18716  LocationNameButton->Enabled = false;
18717  }
18718  ScreenGridButton->Enabled = true;
18719  ExitTrackButton->Enabled = true;
18720  SetGapsButton->Enabled = false;
18721  TrackOKButton->Enabled = false;
18722  if(Track->GapsUnset(5))
18723  {
18724  SetGapsButton->Enabled = true;
18725  }
18726  // only enable if there are gaps still to be set (returns false for no track)
18727  else
18728  {
18729  if(!(Track->NoActiveTrack(2)) && !(Track->IsTrackFinished()))
18730  {
18731  TrackOKButton->Enabled = true;
18732  }
18733  // TrackOK only enabled if track exists, there are no unset gaps, and track not finished
18734  }
18735  SetLengthsButton->Enabled = false;
18736  if(Track->IsTrackFinished()) // can only set lengths for several elements together if TrackFinished
18737  {
18738  SetLengthsButton->Enabled = true;
18739  }
18740  // text buttons
18741  AddTextButton->Enabled = true;
18742  TextOrUserGraphicGridButton->Enabled = true;
18743  FontButton->Enabled = true;
18744  MoveTextOrGraphicButton->Enabled = false;
18745  if(TextHandler->TextVectorSize(9) > 0)
18746  {
18747  MoveTextOrGraphicButton->Enabled = true;
18748  }
18749  if(!Track->UserGraphicVector.empty())
18750  {
18751  MoveTextOrGraphicButton->Enabled = true;
18752  }
18753  SelectionValid = false;
18755  TimetableTitle = "";
18756  SetCaption(0);
18757  } break;
18758 
18759  case PrefDirMode:
18760  Screen->Cursor = TCursor(-11); //Hourglass in case many pref dirs
18764  PrefDirPanel->Visible = true;
18765  PrefDirPanelLabel->Caption = "Preferred direction selection";
18766 
18767  InfoPanel->Visible = true;
18768  InfoPanel->Caption = "PREFERRED DIRECTION SETTING: Select preferred direction start location (right click to erase)";
18769  PrefDirKey->Visible = true;
18770  ModeMenu->Enabled = false;
18771  SigImagePanel->Visible = false; // new at v2.3.0
18772  FileMenu->Enabled = false;
18774  {
18775  ShowMessage("Preferred direction correctness can be difficult to check by eye\n"
18776  "alone, especially for large railways, so an automated conflict\n"
18777  "check is available in the 'Edit' menu when in preferred\n"
18778  "direction mode.\n\n"
18779  "This message will not be shown again.");
18781  }
18782 // set edit menu items
18784  AddPrefDirButton->Enabled = false;
18785  DeleteOnePrefDirButton->Enabled = false;
18787  if(EveryPrefDir->PrefDirSize() > 0)
18788  {
18789  DeleteAllPrefDirButton->Visible = true;
18790  DeleteAllPrefDirButton->Enabled = true;
18791  SaveImageAndPrefDirsMenuItem->Enabled = true;
18792  }
18793  else
18794  {
18795  DeleteAllPrefDirButton->Enabled = false;
18796  SaveImageAndPrefDirsMenuItem->Enabled = false;
18797  }
18798  ExitPrefDirButton->Enabled = true;
18799  ClearandRebuildRailway(33); // to mark PrefDirs & clear earlier PrefDir markers
18800 // TimetableTitle = ""; no need to unload timetable if only PrefDirs being changed
18801 // SetCaption();
18802  Screen->Cursor = TCursor(-2); //Arrow
18803  break;
18804 
18805  case OperMode: // if there are any PrefDirs, set to SigPref, else to NoSigNonPref; start in Paused mode
18810  OperatingPanel->Visible = true;
18811  OperatingPanelLabel->Caption = "Operation";
18812 
18813  CallingOnButton->Visible = false;
18814  PresetAutoSigRoutesButton->Visible = true;
18815  if(!EveryPrefDir->PrefDirVector.empty())//condition added at v2.10.0
18816  {
18817  PresetAutoSigRoutesButton->Enabled = true;
18818  }
18819  else
18820  {
18821  PresetAutoSigRoutesButton->Enabled = false;
18822  }
18823  InfoPanel->Visible = true;
18824  SigImagePanel->Visible = false; // new at v2.3.0
18825  ModeMenu->Enabled = false;
18826  FileMenu->Enabled = false;
18827  EditMenu->Enabled = false;
18828  ImageMenu->Enabled = true;
18829  SaveImageAndGridMenuItem->Enabled = true;
18830  SaveImageNoGridMenuItem->Enabled = true;
18831  if(EveryPrefDir->PrefDirSize() > 0)
18832  {
18833  SaveImageAndPrefDirsMenuItem->Enabled = true;
18834  }
18835  else
18836  {
18837  SaveImageAndPrefDirsMenuItem->Enabled = false;
18838  }
18839  SaveOperatingImageMenuItem->Enabled = true;
18840  AutoSigsFlag = false;
18841  MTBFEditBox->Visible = true; // visible at pre-start whether any value set or not, so can set a value if required
18843  {
18844  MTBFEditBox->Text = AnsiString(TrainController->AvHoursIntValue);
18845  }
18846  else
18847  {
18848  MTBFEditBox->Text = "";
18849  }
18850  MTBFEditBox->ReadOnly = false; // because this is prestart mode
18851  MTBFLabel->Visible = true;
18852  MTBFLabel->Caption = "Mean time between\ntrain failures in\ntimetable hours";
18854  if(EveryPrefDir->PrefDirSize() > 0)
18855  {
18856  ConsecSignalsRoute = true; // default starting conditions
18857  PreferredRoute = true; // default starting conditions
18858  }
18859  else // no PrefDirs
18860  {
18861  ConsecSignalsRoute = false;
18862  PreferredRoute = false;
18863  }
18864  OperateButton->Enabled = true;
18865  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
18866  ExitOperationButton->Enabled = true;
18867  TTClockAdjButton->Enabled = true;
18868  ShowPerfLogForm = false;
18869  PerformanceLogButton->Glyph->LoadFromResourceName(0, "ShowLog");
18870  ShowActionsDueForm = false; // new at v2.2.0
18871  OperatorActionButton->Glyph->LoadFromResourceName(0, "ShowOpActionPanel"); // new v2.2.0
18872 
18874 
18875  Utilities->Clock2Stopped = false;
18879  TTClockSpeed = 1;
18880  TTClockSpeedLabel->Caption = "x1";
18883  Utilities->LastTSRCheckTime = TrainController->TTClockTime; //added at v2.14.0 so don't have any TSRs until 1 min after start
18884  if(Utilities->PerformanceFile.is_open()) //added at v2.17.0 as a safeguard
18885  {
18886  Utilities->PerformanceFile.close();
18887  }
18888  PerformanceFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18889  // format "16/06/2009 20:55:17"
18890  // avoid characters in filename:= / \ : * ? " < > |
18891  PerformanceFileName = CurDir + "\\" + PERFLOG_DIR_NAME + "\\Log " + PerformanceFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18892 
18893  Utilities->PerformanceFile.open(PerformanceFileName.c_str(), std::ios_base::out);
18894  if(Utilities->PerformanceFile.fail())
18895  {
18896  ShowMessage("Performance logfile failed to open, logs won't be saved. Ensure that there is a folder named " + PERFLOG_DIR_NAME +
18897  " in the folder where the 'Railway.exe' program file resides");
18898  }
18899  PerfLogForm->PerformanceLog(16, "Performance Log:"); //these statements separated at v2.13.0 as didn't separate in on-screen log
18900  PerfLogForm->PerformanceLog(21, "Railway: " + RailwayTitle);
18901  PerfLogForm->PerformanceLog(22, "Timetable: " + TimetableTitle);
18902  PerfLogForm->PerformanceLog(23, "Start Time: " + TrainController->TimetableStartTime.FormatString("hh:nn"));
18903  if(Utilities->DelayMode == Nil) //this section added at v2.13.0 for random delays
18904  {
18905  PerfLogForm->PerformanceLog(24, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": No random delays selected");
18906  }
18907  else if(Utilities->DelayMode == Minor)
18908  {
18909  PerfLogForm->PerformanceLog(25, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Minor random delays selected");
18910  }
18911  else if(Utilities->DelayMode == Moderate)
18912  {
18913  PerfLogForm->PerformanceLog(26, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Moderate random delays selected");
18914  }
18915  else if(Utilities->DelayMode == Major)
18916  {
18917  PerfLogForm->PerformanceLog(27, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Major random delays selected");
18918  }
18919  if(Utilities->FailureMode == FNil) //this section added at v2.14.0 for random failure
18920  {
18921  PerfLogForm->PerformanceLog(47, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": No random failures selected");
18922  }
18923  else if(Utilities->FailureMode == FMinor)
18924  {
18925  PerfLogForm->PerformanceLog(48, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Minor random failures selected");
18926  }
18927  else if(Utilities->FailureMode == FModerate)
18928  {
18929  PerfLogForm->PerformanceLog(49, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Moderate random failures selected");
18930  }
18931  else if(Utilities->FailureMode == FMajor)
18932  {
18933  PerfLogForm->PerformanceLog(50, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Major random failures selected");
18934  }
18936 // DisableRouteButtons(2); enable route setting or pre-start
18937 // DisablePanelsStoreMainMenuStates();
18938  TrainController->ContinuationAutoSigVector.clear(); // for restarting after earlier run
18939  AllRoutes->LockedRouteVector.clear(); // for restarting after earlier run
18940 // TrainController->Operate(1);//plot trains that are present at TT start time, ready for running - no, allow route plotting prior to train entries
18941 
18942 // reset all performance indicators
18951  TrainController->OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
18972 
18973  TrainController->OpActionPanelHintDelayCounter = 0; // new at v2.2.0 to reset hint delay
18975  ActionsDueForm->ActionsDueListBox->Items->Add(L""); // hints for OpActionPanel
18976  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18977  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18978  ActionsDueForm->ActionsDueListBox->Items->Add(L"Left click");
18979  ActionsDueForm->ActionsDueListBox->Items->Add(L"headcode to");
18980  ActionsDueForm->ActionsDueListBox->Items->Add(L"locate train");
18981  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18982  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18983  ActionsDueForm->ActionsDueListBox->Items->Add(L"Right click");
18984  ActionsDueForm->ActionsDueListBox->Items->Add(L"headcode for");
18985  ActionsDueForm->ActionsDueListBox->Items->Add(L"information");
18986  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18987  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
18988  ActionsDueForm->ActionsDueListBox->Items->Add(L"Left click and");
18989  ActionsDueForm->ActionsDueListBox->Items->Add(L"hold \"Actions");
18990  ActionsDueForm->ActionsDueListBox->Items->Add(L"Due\" label");
18991  ActionsDueForm->ActionsDueListBox->Items->Add(L"to move panel");
18992  DelayMenu->Visible = true; //the following added at v2.13.0
18993  DelayMenu->Enabled = true;
18994  FailureMenu->Visible = true; //the following added at v2.14.0
18995  FailureMenu->Enabled = true;
18996  //create a new StaticFeaturesDisplay, populate it in Clearand...
18998  StaticFeaturesScreen->Canvas->Brush->Style = bsClear; // so text prints transparent
18999  StaticFeaturesScreen->Canvas->Brush->Color = Utilities->clTransparent;
19000  StaticFeaturesScreen->Canvas->FillRect(TRect(0, 0, MainScreen->Width, MainScreen->Height)); //fill it with transparent colour
19001  ClearandRebuildRailway(55); // so points display with one fillet //moved to after StaticFeatures... cleared to set it up correctly
19002  break;
19003 
19004  case RestartSessionOperMode: // restart in Paused mode after a session load, sets both Level1Mode & Level2OperMode
19005  Level1Mode = OperMode;
19006 // Level2OperMode = Paused; this is now loaded during LoadInterface & could be PreStart of Paused
19010  OperatingPanel->Visible = true;
19011  OperatingPanelLabel->Caption = "Operation";
19012  DelayMenu->Visible = true; //added at v2.13.0
19013  DelayMenu->Enabled = true;
19014  FailureMenu->Visible = true; //added at v2.14.0
19015  FailureMenu->Enabled = true;
19016  CallingOnButton->Visible = true;
19017  PresetAutoSigRoutesButton->Visible = false;
19018  InfoPanel->Visible = true;
19019  ModeMenu->Enabled = false;
19020  SigImagePanel->Visible = false; // new at v2.3.0
19021  FileMenu->Enabled = false;
19022  EditMenu->Enabled = false;
19023  ImageMenu->Enabled = true;
19024  SaveImageAndGridMenuItem->Enabled = true;
19025  SaveImageNoGridMenuItem->Enabled = true;
19026  if(EveryPrefDir->PrefDirSize() > 0)
19027  {
19028  SaveImageAndPrefDirsMenuItem->Enabled = true;
19029  }
19030  else
19031  {
19032  SaveImageAndPrefDirsMenuItem->Enabled = false;
19033  }
19034  SaveOperatingImageMenuItem->Enabled = true;
19035 
19036  FlashControlButton->Glyph->LoadFromResourceName(0, "NoFlash"); //these added at v2.17.0 so reloads with normal flashing
19037  StopFlashFlag = false;
19038 
19039  OperateButton->Enabled = true;
19040  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
19041  ExitOperationButton->Enabled = true;
19042  TTClockAdjButton->Enabled = true;
19045  if(Level2OperMode == Paused)
19046  {
19047  DisableRouteButtons(3); // could be PreStart or Paused
19048  }
19052  Utilities->LastTSRCheckTime = TrainController->TTClockTime; //added at v2.14.0 so don't have any TSRs until 1 min after start
19054  TTClockSpeed = 1;
19055  TTClockSpeedLabel->Caption = "x1";
19057  ShowPerfLogForm = false; // original (ShowPerformancePanel) added at v2.2.0, changed at v2.13.0
19058  ShowActionsDueForm = false; // new at v2.2.0
19059  TrainController->OpActionPanelHintDelayCounter = 0; // new at v2.2.0 to reset hint delay
19061  ActionsDueForm->ActionsDueListBox->Items->Add(L""); // hints for OpActionPanel
19062  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19063  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19064  ActionsDueForm->ActionsDueListBox->Items->Add(L"Left click");
19065  ActionsDueForm->ActionsDueListBox->Items->Add(L"headcode to");
19066  ActionsDueForm->ActionsDueListBox->Items->Add(L"locate train");
19067  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19068  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19069  ActionsDueForm->ActionsDueListBox->Items->Add(L"Right click");
19070  ActionsDueForm->ActionsDueListBox->Items->Add(L"headcode for");
19071  ActionsDueForm->ActionsDueListBox->Items->Add(L"information");
19072  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19073  ActionsDueForm->ActionsDueListBox->Items->Add(L"");
19074  ActionsDueForm->ActionsDueListBox->Items->Add(L"Left click and");
19075  ActionsDueForm->ActionsDueListBox->Items->Add(L"hold \"Actions");
19076  ActionsDueForm->ActionsDueListBox->Items->Add(L"Due\" label");
19077  ActionsDueForm->ActionsDueListBox->Items->Add(L"to move panel");
19078  //create a new StaticFeaturesDisplay, populate it in Clearand...
19080  StaticFeaturesScreen->Canvas->Brush->Style = bsClear; // so text prints transparent
19081  StaticFeaturesScreen->Canvas->Brush->Color = Utilities->clTransparent;
19082  StaticFeaturesScreen->Canvas->FillRect(TRect(0, 0, MainScreen->Width, MainScreen->Height)); //fill it with transparent colour
19083  if((TrainController->AvHoursIntValue > 0) || (Level2OperMode == PreStart)) // only visible if already set or if still in prestart mode
19084  {
19085  MTBFEditBox->Visible = true;
19087  {
19088  MTBFEditBox->Text = AnsiString(TrainController->AvHoursIntValue);
19089  }
19090  else
19091  {
19092  MTBFEditBox->Text = "";
19093  }
19094  MTBFEditBox->ReadOnly = false; // because this is still prestart mode
19095  MTBFLabel->Visible = true;
19096  MTBFLabel->Caption = "Mean time between\ntrain failures in\ntimetable hours";
19098  }
19099  else
19100  {
19101  MTBFEditBox->Visible = false;
19102  MTBFEditBox->Text = "";
19103  MTBFEditBox->ReadOnly = true; // because this is not prestart mode
19104  MTBFLabel->Visible = false;
19105  MTBFLabel->Caption = "Mean time between\ntrain failures in\ntimetable hours";
19107  }
19108  ClearandRebuildRailway(104); // after StaticFeatures cleared to set it up correctly
19109  break;
19110 
19111  default:
19112  // No further recursion in BaseMode so OK
19113  Level1Mode = BaseMode;
19114  SetLevel1Mode(29);
19115  break;
19116  }
19117  api_main_mode_ = int(Level1Mode); //added at v2.10.0
19118  session_api_->dump(); // update session INI file
19119  Utilities->CallLogPop(103);
19120 }
19121 
19122 // ---------------------------------------------------------------------------
19123 
19125 {
19126  try
19127  {
19128  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetLevel2TrackMode");
19129  if(Level1Mode != TrackMode)
19130  {
19131  // No further recursion in BaseMode so OK
19132  Level1Mode = BaseMode;
19133  SetLevel1Mode(20);
19134  Utilities->CallLogPop(1115);
19135  return;
19136  }
19138  {
19139  Utilities->CallLogPop(104);
19140  return;
19141  }
19142  switch(Level2TrackMode) // use the data member
19143  {
19144  case AddTrack:
19146  InfoPanel->Visible = true;
19147  InfoPanel->Caption = "ADDING TRACK: Select element then left click to add it. Right click an element to remove it.";
19148  LengthConversionPanel->Visible = false; // in case had been in distance setting mode
19149  SpeedConversionPanel->Visible = false; // in case had been in distance setting mode
19150  TrackElementPanel->Visible = true;
19151  TrackElementPanel->Enabled = true;
19152  SigAspectButton->Visible = true;
19153  SigAspectButton->Enabled = true;
19154  ClearandRebuildRailway(34); // to replot grid if required & clear any other unwanted items
19156  SetLengthsButton->Enabled = false;
19157  // section added at v2.8.0 so buttons show correctly after a paste
19158  SetGapsButton->Enabled = false;
19159  TrackOKButton->Enabled = false;
19160  if(Track->GapsUnset(9))
19161  {
19162  SetGapsButton->Enabled = true;
19163  }
19164  // only enable if there are gaps still to be set (returns false for no track)
19165  else
19166  {
19167  if(!(Track->NoActiveTrack(3)) && !(Track->IsTrackFinished()))
19168  {
19169  TrackOKButton->Enabled = true;
19170  }
19171  // TrackOK only enabled if track exists, there are no unset gaps, and track not finished
19172  }
19173  // end of 2.8.0 addition
19174  if(Track->IsTrackFinished()) // can only set lengths for several elements together if TrackFinished
19175  {
19176  SetLengthsButton->Enabled = true;
19177  }
19178  UserGraphicReselectPanel->Visible = false;
19179  SelectLengthsFlag = false; // in case still set though probably won't be
19180  EditMenu->Enabled = true; // added at v2.6.0 to allow edits for an empty screen so track elements can fill a selected area
19181  SetTrackModeEditMenu(6); //added at v2.10.0 to set menu items
19182  break;
19183 
19184  case AddGraphic:
19185  InfoPanel->Visible = true;
19186  InfoPanel->Caption = "ADDING GRAPHIC: Left click layout to add SELECTED graphic, right click to remove ANY graphic.";
19187  break;
19188 
19189  case SelectGraphic:
19190  InfoPanel->Visible = true;
19191  InfoPanel->Caption = "SELECTING USER GRAPHIC: Select the graphic file then add as many as necessary to the layout.";
19192  break;
19193 
19194  case GapSetting:
19195  int HLoc, VLoc, Count;
19196  Count = Track->NumberOfGaps(0);
19197  if(div(Count, 2).rem == 1) // condition OK
19198  {
19199  ShowMessage("Can't connect, there are an odd number of gaps");
19201  SetLevel1Mode(77);
19203  // No further recursion in AddTrack so OK
19204  SetLevel2TrackMode(40);
19205  Utilities->CallLogPop(105);
19206  return;
19207  }
19208  if(!HighLightOneGap(2, HLoc, VLoc)) // condition OK
19209  // need to call this here to start gap setting process off,
19210  // called in MainScreenMouseDown hereafter. Function returns false for either a LocError (links not yet
19211  // complete) or no more gaps to be highlighted
19212  {
19213  // shouldn't reach here as later gaps covered in MainScreenMouseDown but leave & give error message
19214  ShowMessage("Error - Even number of gaps but all set after first call to HighLightOneGap");
19216  SetLevel1Mode(78);
19218  // No further recursion in AddTrack so OK
19219  SetLevel2TrackMode(41);
19220  Utilities->CallLogPop(106);
19221  return; // all gaps set
19222  }
19223  InfoPanel->Visible = true;
19224  InfoPanel->Caption = "CONNECTING GAPS: Click on connecting gap";
19225  UserGraphicReselectPanel->Visible = false;
19227  break;
19228 
19229  case AddText:
19230  InfoPanel->Visible = true;
19231  InfoPanel->Caption = "ADDING/EDITING TEXT: Left click to add, right click first letter to erase, or left click first letter to edit";
19232  if(TextHandler->TextVectorSize(13) > 0)
19233  {
19234  MoveTextOrGraphicButton->Enabled = true;
19235  }
19236  else
19237  {
19238  MoveTextOrGraphicButton->Enabled = false;
19239  }
19240  UserGraphicReselectPanel->Visible = false;
19241  ClearandRebuildRailway(58); // to drop DistanceKey if was displayed
19242  break;
19243 
19244  case MoveTextOrGraphic:
19245  InfoPanel->Visible = true;
19246  InfoPanel->Caption = "MOVING TEXT OR GRAPHIC: If text left click first letter, if graphic left click anywhere, then drag";
19247  UserGraphicReselectPanel->Visible = false;
19248  ClearandRebuildRailway(59); // to drop DistanceKey if was displayed
19249  break;
19250 
19251  case AddLocationName:
19252  InfoPanel->Visible = true;
19253  InfoPanel->Caption = "NAMING LOCATIONS: Click on location element to add or change name";
19254  ClearandRebuildRailway(35); // to get rid of earlier red rectangle
19255  UserGraphicReselectPanel->Visible = false;
19256  SetTrackBuildImages(12);
19257  break;
19258 
19259  case DistanceStart:
19260  InfoPanel->Visible = true;
19261  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Select first location (only non-default elements marked)";
19262  ExitHeatmaps(); //to initialise all parameters
19263  DistanceKey->Visible = true;
19264  LengthHeatmapBitBtn->Visible = true;
19265  SpeedHeatmapBitBtn->Visible = true;
19266  LengthHeatmapBitBtn->Enabled = true;
19267  SpeedHeatmapBitBtn->Enabled = true;
19268  LengthConversionPanel->Visible = true;
19269  SpeedConversionPanel->Visible = true;
19270  UserGraphicReselectPanel->Visible = false;
19271  ClearandRebuildRailway(36); // to get rid of earlier unwanted markings
19272  break;
19273 
19274  case DistanceContinuing:
19275  InfoPanel->Visible = true;
19276  ExitHeatmaps(); //to initialise all parameters
19277  LengthHeatmapBitBtn->Visible = true;
19278  SpeedHeatmapBitBtn->Visible = true;
19279  LengthHeatmapBitBtn->Enabled = false;
19280  SpeedHeatmapBitBtn->Enabled = false;
19281  if(ConstructPrefDir->PrefDirSize() == 1)
19282  {
19283  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Select next location";
19284  }
19285  else
19286  {
19287  InfoPanel->Caption = "DISTANCE/SPEED SETTING: Continue or set values (overall length), or right click to cancel/truncate";
19288  }
19289  UserGraphicReselectPanel->Visible = false;
19290  ClearandRebuildRailway(54); // to remove earlier end marker if present
19291  break;
19292 
19293  case TrackSelecting:
19294  Track->CopyFlag = false;
19295  if(!SelectionValid)
19296  {
19297  ResetSelectRect(); // so a viewpoint change before a new SelectRect chosen doesn't redisplay
19298  }
19299  // the old SelectRect (only called when entered from SelectMenuItemClick, & not from
19300  // ReselectMenuItemClick)
19301  InfoPanel->Visible = true;
19302  InfoPanel->Caption = "SELECTING: Select area - click left mouse && drag";
19303  SelectMenuItem->Enabled = false;
19304  ReselectMenuItem->Enabled = false;
19305  CancelSelectionMenuItem->Enabled = true;
19306  UserGraphicReselectPanel->Visible = false;
19307  break;
19308 
19309  case CopyMoving:
19310  Track->CopyFlag = true;
19311  InfoPanel->Visible = true;
19312  InfoPanel->Caption = "COPYING: Left click in selection && drag";
19313  CutMenuItem->Enabled = false;
19314  CopyMenuItem->Enabled = false;
19315  FlipMenuItem->Enabled = false;
19316  MirrorMenuItem->Enabled = false;
19317  RotRightMenuItem->Enabled = false;
19318  RotLeftMenuItem->Enabled = false;
19319  RotateMenuItem->Enabled = false;
19320  PasteMenuItem->Enabled = true;
19321  // PasteWithAttributesMenuItem->Enabled = false; //new at v2.2.0 - don't allow the option if copying (dropped at 2.4.0 as all pastes are with attributes)
19322  DeleteMenuItem->Enabled = false;
19323  SelectLengthsMenuItem->Enabled = false;
19324  SelectBiDirPrefDirsMenuItem->Visible = false;
19325  CheckPrefDirConflictsMenuItem->Visible = false;
19326  CancelSelectionMenuItem->Enabled = true;
19330  UserGraphicReselectPanel->Visible = false;
19331  break;
19332 
19333  case CutMoving:
19334  {
19335  // have to use braces as otherwise the default case bypasses the initialisation of these local variables
19336  // erase track elements within selected region
19337  Track->CopyFlag = false;
19338  bool EraseSuccessfulFlag, NeedToLink = false, TextChangesMade = false, GraphicChangesMade = false;
19339  int ErasedTrackVectorPosition;
19340  Screen->Cursor = TCursor(-11); // Hourglass;
19341  InfoPanel->Visible = true;
19342  InfoPanel->Caption = "CUT PROCESSING: Please do not click the mouse";
19343  InfoPanel->Update();
19344  for(int H = SelectRect.left; H < SelectRect.right; H++)
19345  {
19346  for(int V = SelectRect.top; V < SelectRect.bottom; V++)
19347  {
19348  Track->EraseTrackElement(2, H, V, ErasedTrackVectorPosition, EraseSuccessfulFlag, false);
19349  if(EraseSuccessfulFlag)
19350  {
19351  if(ErasedTrackVectorPosition > -1) //may be an inactive element that was erased
19352  {
19353  EveryPrefDir->RealignAfterTrackErase(1, ErasedTrackVectorPosition);
19354  }
19355  NeedToLink = true;
19356  }
19357  }
19358  }
19359 
19360  // erase text elements within selected region
19361  int LowSelectHPos = SelectRect.left * 16;
19362  int HighSelectHPos = SelectRect.right * 16;
19363  int LowSelectVPos = SelectRect.top * 16;
19364  int HighSelectVPos = SelectRect.bottom * 16;
19365  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
19366  {
19367  for(TTextHandler::TTextVectorIterator TextPtr = (TextHandler->TextVector.end() - 1); TextPtr >= TextHandler->TextVector.begin();
19368  TextPtr--) // reverse to prevent skipping during erase
19369  {
19370  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos <
19371  HighSelectVPos))
19372  {
19373  if(TextHandler->TextErase(1, TextPtr->HPos, TextPtr->VPos, AnsiString("")))
19374  {
19375  ;
19376  } // unused condition
19377 
19378  TextChangesMade = true;
19379  }
19380  }
19381  }
19382  // erase graphic elements that fall wholly within region to be overwritten
19383  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
19384  {
19385  for(TTrack::TUserGraphicVector::iterator GraphicPtr = (Track->UserGraphicVector.end() - 1); GraphicPtr >= Track->UserGraphicVector.begin();
19386  GraphicPtr--) // reverse to prevent skipping during erase
19387  {
19388  if((GraphicPtr->HPos >= LowSelectHPos) && ((GraphicPtr->HPos + GraphicPtr->Width) < HighSelectHPos) && (GraphicPtr->VPos >= LowSelectVPos)
19389  && ((GraphicPtr->VPos + GraphicPtr->Height) < HighSelectVPos))
19390  {
19391  Track->UserGraphicVector.erase(GraphicPtr);
19392  GraphicChangesMade = true;
19393  }
19394  }
19395  }
19396  Track->CheckMapAndTrack(11); // test
19397  Track->CheckMapAndInactiveTrack(10); // test
19398  Track->CheckLocationNameMultiMap(19); // test
19399  Screen->Cursor = TCursor(-2); // Arrow;
19400  // Track->SetTrackFinished(!NeedToLink); This is an error (see Sam Wainwright email of 24/08/17 & devhistory.txt
19401  // if track not linked to begin with then becomes linked if NeedToLink false
19402  if(NeedToLink)
19403  {
19404  Track->SetTrackFinished(false); // corrected for v2.1.0
19405  }
19406  InfoPanel->Caption = "CUTTING: Left click in selection && drag";
19407  CutMenuItem->Enabled = false;
19408  CopyMenuItem->Enabled = false;
19409  FlipMenuItem->Enabled = false;
19410  MirrorMenuItem->Enabled = false;
19411  RotRightMenuItem->Enabled = false;
19412  RotLeftMenuItem->Enabled = false;
19413  RotateMenuItem->Enabled = false;
19414  PasteMenuItem->Enabled = true;
19415  // PasteWithAttributesMenuItem->Enabled = true; //new at v2.2.0 - option enabled if cutting (dropped at 2.4.0 as all pastes are with attributes)
19416  DeleteMenuItem->Enabled = false;
19417  SelectLengthsMenuItem->Enabled = false;
19418  SelectBiDirPrefDirsMenuItem->Visible = false;
19419  CheckPrefDirConflictsMenuItem->Visible = false;
19420  CancelSelectionMenuItem->Enabled = true;
19423  if(NeedToLink || TextChangesMade || GraphicChangesMade)
19424  {
19425  ResetChangedFileDataAndCaption(20, true); // true for NonPrefDirChangesMade
19426  }
19427  ClearandRebuildRailway(37); // to overplot the erased elements with SelectBitmap
19428  UserGraphicReselectPanel->Visible = false;
19430  } break;
19431 
19432  case Pasting:
19433  {
19434  // have to use braces as otherwise the default case bypasses the initialisation of these local variables
19435  int HDiff = SelectBitmapHLoc - SelectRect.left; // SelectBitmapHLoc/VLoc is the paste position & SelectRect.left/top is the original position
19436  int VDiff = SelectBitmapVLoc - SelectRect.top;
19437  if(!SelectionValid && !CancelSelectionFlag) // may be pasting into a new application so use clipboard, new at v2.8.0
19438  {
19439  bool ValidResult;
19440  RecoverClipboard(0, ValidResult); // new at v2.8.0
19441  if(ValidResult)
19442  {
19443  HDiff = Display->DisplayOffsetH - SelectRect.left; // SelectRect.left & top recovered in clipboard
19444  VDiff = Display->DisplayOffsetV - SelectRect.top;
19445  SelectBitmapHLoc = Display->DisplayOffsetH; // so the area to erase corresponds to the paste area (TLHC of screen = DisplayOffsetH & V)
19447  SelectionValid = false; // don't want reselect in new app after paste
19448  Track->SetTrackFinished(false); // would be set to false in other app but not in this so set it to false here
19450  {
19451  UnicodeString MessageStr =
19452  "Please be aware of the relevant conditions when pasting a railway segment from a different application.\n"
19453  "These are set out in section 3.5 of the manual and " "on-screen help under the heading 'Pasting in an application "
19454  "after cutting or copying from a different application'.\n\n" "This warning will not be shown again.\n\n" "Proceed?";
19455  int button = Application->MessageBox(MessageStr.c_str(), L"Warning", MB_YESNO | MB_ICONWARNING);
19457  if(button == IDNO)
19458  {
19459  CancelSelectionMenuItem->Click(); // reset clipboard etc
19460  Utilities->CallLogPop(2271);
19461  return;
19462  }
19463  }
19464  }
19465  else
19466  {
19467  Application->MessageBoxW(L"Unable to paste (clipboard is invalid) - possibly because another application has changed it)", L"Warning", MB_OK | MB_ICONWARNING);
19468  CancelSelectionMenuItem->Click(); // reset clipboard etc
19469  TrainController->LogEvent("ValidResult false in case Pasting - probably due to error in RecoverClipboard - see earlier log");
19470  Utilities->CallLogPop(2272); //EClipboardException (here as search term only)
19471  return;
19472  }
19473  }
19474  if(CancelSelectionFlag) // plot cut area in original position in case moved
19475  {
19478  HDiff = 0;
19479  VDiff = 0;
19480  }
19481  Clipboard()->Clear(); // already cleared & closed if recovered clipboard but not otherwise so clear & close here
19482  Clipboard()->Close();
19485  bool NeedToLink = false;
19486  bool TrackLinkingRequiredFlag;
19487  Screen->Cursor = TCursor(-11); // Hourglass;
19488  InfoPanel->Visible = true;
19489  InfoPanel->Caption = "PASTING: Please wait";
19490  InfoPanel->Update();
19491  // erase track elements
19492  int LowSelectHLoc = SelectBitmapHLoc;
19493  int HighSelectHLoc = SelectBitmapHLoc + (SelectBitmap->Width / 16);
19494  int LowSelectVLoc = SelectBitmapVLoc;
19495  int HighSelectVLoc = SelectBitmapVLoc + (SelectBitmap->Height / 16);
19496  bool TrackEraseSuccessfulFlag; // needed but not used here
19497  int ErasedTrackVectorPosition;
19498  // new quick method of erasing, only need H & V values
19499  for(int x = LowSelectHLoc; x < HighSelectHLoc; x++)
19500  {
19501  for(int y = LowSelectVLoc; y < HighSelectVLoc; y++)
19502  {
19503  Track->EraseTrackElement(5, x, y, ErasedTrackVectorPosition, TrackEraseSuccessfulFlag, false);
19504  if(ErasedTrackVectorPosition > -1)
19505  {
19506  EveryPrefDir->RealignAfterTrackErase(2, ErasedTrackVectorPosition);
19507  }
19508  }
19509  }
19510 
19511  // erase text elements that fall within region to be overwritten
19512  int LowSelectHPos = SelectBitmapHLoc * 16;
19513  int HighSelectHPos = (SelectBitmapHLoc * 16) + SelectBitmap->Width;
19514  int LowSelectVPos = SelectBitmapVLoc * 16;
19515  int HighSelectVPos = (SelectBitmapVLoc * 16) + SelectBitmap->Height;
19516  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
19517  {
19518  for(TTextHandler::TTextVectorIterator TextPtr = (TextHandler->TextVector.end() - 1); TextPtr >= TextHandler->TextVector.begin();
19519  TextPtr--) // reverse to prevent skipping during erase
19520  {
19521  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos <
19522  HighSelectVPos))
19523  {
19524  if(TextHandler->TextErase(2, TextPtr->HPos, TextPtr->VPos, AnsiString("")))
19525  {
19526  ;
19527  } // unused condition
19528 
19529  }
19530  }
19531  }
19532  // erase graphic elements that fall wholly within region to be overwritten
19533  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
19534  {
19535  for(TTrack::TUserGraphicVector::iterator GraphicPtr = (Track->UserGraphicVector.end() - 1); GraphicPtr >= Track->UserGraphicVector.begin();
19536  GraphicPtr--) // reverse to prevent skipping during erase
19537  {
19538  if((GraphicPtr->HPos >= LowSelectHPos) && ((GraphicPtr->HPos + GraphicPtr->Width) < HighSelectHPos) && (GraphicPtr->VPos >= LowSelectVPos)
19539  && ((GraphicPtr->VPos + GraphicPtr->Height) < HighSelectVPos))
19540  {
19541  Track->UserGraphicVector.erase(GraphicPtr);
19542  }
19543  }
19544  }
19545  // change the H & V values in SelectVector to the new positions in case Reselect chosen
19546  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
19547  {
19548  Track->SelectVectorAt(35, x).HLoc += HDiff;
19549  Track->SelectVectorAt(1, x).VLoc += VDiff;
19550  }
19551 
19552  // add the new track elements
19553  AnsiString LocName;
19554  for(unsigned int x = 0; x < Track->SelectVectorSize(); x++)
19555  {
19556  if(Track->CopyFlag) // blank all names if copying, lengths & speedlimits stay
19557  {
19558  Track->SelectVectorAt(80, x).LocationName = "";
19560  }
19561  else //cut, check if element has a name and if so remove same existing name from name map and track vector
19562  {
19563  LocName = Track->SelectVectorAt(82, x).LocationName;
19564  if(LocName == "")
19565  {
19566  LocName = Track->SelectVectorAt(83, x).ActiveTrackElementName;
19567  }
19568  if(LocName != "")
19569  {
19570  Track->EraseLocationAndActiveTrackElementNames(6, LocName); //this will keep erasing adjacent element names but the last one will be pasted and not erased
19571  //and that will name all linked elements so should work ok
19572  int HPos = 0, VPos = 0;
19573  if(TextHandler->FindText(5, LocName, HPos, VPos))
19574  {
19575  ;
19576  }
19577  {
19578  if(EraseLocationNameText(4, LocName, HPos, VPos))
19579  {
19580  ;
19581  } // condition not used
19582  }
19583  }
19584  }
19585  bool InternalChecks = false;
19586  // if(Track->PastingWithAttributes) //new at v2.2.0 to select the new funtion & skip multimap checks //drop in v2.4.0
19587  // {
19589  TrackLinkingRequiredFlag, InternalChecks);
19590  // new at v2.2.0 & used in place of PlotAndAddTrackElement to keep length & speed values
19591  // }
19592  /* drop this in v2.4.0 as all pastes are with attributes
19593  else //'Aspect' parameter added to PlotAndAdd... at v2.2.0 so can plot signals correctly (always four-aspect before)
19594  {
19595  int Aspect;
19596  if(Track->SelectVectorAt(15, x).TrackType != SignalPost) Aspect = 0; //if an '0' value appears with a SignalPost then must be adding track
19597  //this combination allows the funtion to distinguish between adding track and plotting with attributes
19598  else if(Track->SelectVectorAt(16, x).SigAspect == TTrackElement::GroundSignal) Aspect = 1;
19599  else if(Track->SelectVectorAt(17, x).SigAspect == TTrackElement::TwoAspect) Aspect = 2;
19600  else if(Track->SelectVectorAt(18, x).SigAspect == TTrackElement::ThreeAspect) Aspect = 3;
19601  else Aspect = 4;
19602  Track->PlotAndAddTrackElement(2, Track->SelectVectorAt(19, x).SpeedTag, Aspect, Track->SelectVectorAt(20, x).HLoc, Track->SelectVectorAt(21, x).VLoc, TrackLinkingRequiredFlag, InternalChecks);
19603  }
19604  */
19605  if(TrackLinkingRequiredFlag)
19606  {
19607  NeedToLink = true;
19608  }
19609  }
19610 
19611  //add the pref dir elements, added at v2.9.0
19612  int ATVecPos;
19613  bool FoundFlag;
19614  if(SelectPrefDir->PrefDirSize() > 0) // skip iteration if empty
19615  {
19616  // keep contents valid in case reselect
19618  PDVIt++)
19619  {
19620  PDVIt->HLoc += HDiff; // for reselect
19621  PDVIt->VLoc += VDiff; // for reselect
19622  //need to reset TrackVectorPosition in case any elements erased before linking, as if so EveryPrefDir can only be erased too if it has the correct TrackVectorPosition
19623  ATVecPos = Track->GetVectorPositionFromTrackMap(60, PDVIt->HLoc, PDVIt->VLoc, FoundFlag);
19624  if(!FoundFlag)
19625  {
19626  throw Exception("Failed to find TrackVectorPosition in PrefDir setting for Paste");
19627  }
19628  PDVIt->SetTrackVectorPosition(ATVecPos);
19629 
19630  //reset all Conns & ConnLinkPos values so won't be erased during a later cut, they will be set in RebuildPrefDirVector which is called when track linked
19631  for(int x = 0; x < 4; x++)
19632  {
19633  PDVIt->Conn[x] = -1;
19634  PDVIt->ConnLinkPos[x] = -1;
19635  }
19637  }
19639  }
19640 
19641  // add the new text items
19642  if(!TextHandler->SelectTextVector.empty()) // skip iteration if empty else have an error
19643  {
19644  for(TTextHandler::TTextVectorIterator TextPtr = TextHandler->SelectTextVector.begin(); TextPtr < TextHandler->SelectTextVector.end(); TextPtr++)
19645  {
19646  TextPtr->HPos += HDiff * 16;
19647  TextPtr->VPos += VDiff * 16;
19648  AnsiString TempString = TextPtr->TextString;
19649  // have to create a new TextItem in order to create a new Font object
19650  /* drop in v2.4.0 as all pastes are paste with attributes
19651  if(!Track->PastingWithAttributes) //new at v2.2.0 to deal with the new location prefix '##**' //drop in v2.4.0
19652  {
19653  if(TextPtr->TextString.SubString(1,4) != "##**") //added for named locations so can delete in a simple paste but
19654  //use in PastingWithAttributes
19655  {
19656  TTextItem TextItem(TextPtr->HPos, TextPtr->VPos, TextPtr->TextString, TextPtr->Font);
19657  TextHandler->TextVectorPush(0, TextItem); //if a normal paste include normal text but not location text
19658  }
19659  else TextPtr->TextString = ""; //delete the name for a simple paste
19660  }
19661  */
19662  // else //if pasting with attributes paste all text but strip the '##**' prefix if present
19663  // {
19664  if(TextPtr->TextString.SubString(1, 4) == "##**")
19665  {
19666  TempString = TextPtr->TextString.SubString(5, TextPtr->TextString.Length()); // don't change SelectTextVector value
19667  if(Track->CopyFlag)
19668  {
19669  TextPtr->TextString = ""; // change SelectTextVector value as reselect shouldn't have locations if copied
19670  TempString = "";
19671  }
19672  }
19673  TTextItem TextItem(TextPtr->HPos, TextPtr->VPos, TempString, TextPtr->Font);
19675  // }
19676  }
19677  }
19678  // add new graphic items
19679  if(!Track->SelectGraphicVector.empty()) // skip iteration if empty else have an error
19680  {
19681  // keep contents of SelectVector valid in case reselect
19682  for(TTrack::TUserGraphicVector::iterator GraphicPtr = Track->SelectGraphicVector.begin(); GraphicPtr < Track->SelectGraphicVector.end();
19683  GraphicPtr++)
19684  {
19685  GraphicPtr->HPos += HDiff * 16; // for reselect
19686  GraphicPtr->VPos += VDiff * 16; // for reselect
19687  Track->UserGraphicVector.push_back(*GraphicPtr);
19688  }
19689  }
19690  Track->SkipLocationNameMultiMapCheck = false; // renamed in v2.4.0 - reset the flag after pasting complete, otherwise multimap checks always skipped
19691  Track->CopyFlag = false;
19692  Track->CheckMapAndTrack(7); // test
19693  Track->CheckMapAndInactiveTrack(7); // test
19694  Track->CheckLocationNameMultiMap(7); // test
19695  // Track->SetTrackFinished(!NeedToLink); This is an error (see Sam Wainwright email of 24/08/17 & devhistory.txt
19696  // if track not linked to begin with then becomes linked if NeedToLink false
19697  if(NeedToLink)
19698  {
19699  Track->SetTrackFinished(false); // corrected for v2.1.0
19700  }
19701  Screen->Cursor = TCursor(-2); // Arrow;
19702  SetTrackBuildImages(14);
19704  // Level1Mode = TrackMode; //dropped as prevents AddTrack being called to display track elements
19705  // SetLevel1Mode(79);
19706  SetTrackModeEditMenu(5); // this is called from the above but is still needed to enable Select (& Reselect if pasted in same app) menu items
19707  PasteMenuItem->Enabled = false;
19708  UserGraphicReselectPanel->Visible = false;
19709  if(Level2TrackMode != AddTrack) // no need to set if already set in an earlier call
19710  {
19712  // No further recursion in AddTrack so OK
19713  SetLevel2TrackMode(42);
19714  }
19715  } break;
19716 
19717  case Deleting:
19718  {
19719  // have to use braces as otherwise the default case bypasses the initialisation of these local variables
19720  Track->CopyFlag = false;
19721  UnicodeString MessageStr = "Selected area will be deleted - proceed?";
19722  int button = Application->MessageBox(MessageStr.c_str(), L"Please confirm", MB_YESNO);
19723  if(button == IDNO)
19724  {
19725  break;
19726  }
19727  bool EraseSuccessfulFlag, NeedToLink = false, TextChangesMade = false, GraphicChangesMade = false;
19728  int ErasedTrackVectorPosition;
19729  Screen->Cursor = TCursor(-11); // Hourglass;
19730  InfoPanel->Visible = true;
19731  InfoPanel->Caption = "DELETING: Please wait";
19732  InfoPanel->Update();
19733  for(int H = SelectRect.left; H < SelectRect.right; H++)
19734  {
19735  for(int V = SelectRect.top; V < SelectRect.bottom; V++)
19736  {
19737  Track->EraseTrackElement(3, H, V, ErasedTrackVectorPosition, EraseSuccessfulFlag, false);
19738  if(EraseSuccessfulFlag)
19739  {
19740  if(ErasedTrackVectorPosition > -1)
19741  {
19742  EveryPrefDir->RealignAfterTrackErase(3, ErasedTrackVectorPosition);
19743  }
19744  NeedToLink = true;
19745  }
19746  }
19747  }
19748  // erase text elements that fall within selected region
19749  int LowSelectHPos = SelectRect.left * 16;
19750  int HighSelectHPos = SelectRect.right * 16;
19751  int LowSelectVPos = SelectRect.top * 16;
19752  int HighSelectVPos = SelectRect.bottom * 16;
19753  if(!TextHandler->TextVector.empty()) // skip iteration if empty else have an error
19754  {
19755  for(TTextHandler::TTextVectorIterator TextPtr = (TextHandler->TextVector.end() - 1); TextPtr >= TextHandler->TextVector.begin();
19756  TextPtr--) // reverse to prevent skipping during erase
19757  {
19758  AnsiString Check = TextPtr->TextString;
19759  if((TextPtr->HPos >= LowSelectHPos) && (TextPtr->HPos < HighSelectHPos) && (TextPtr->VPos >= LowSelectVPos) && (TextPtr->VPos <
19760  HighSelectVPos))
19761  {
19762  if(TextHandler->TextErase(3, TextPtr->HPos, TextPtr->VPos, AnsiString("")))
19763  {
19764  ;
19765  } // unused condition
19766 
19767  TextChangesMade = true;
19768  }
19769  }
19770  }
19771  // erase graphic elements that fall within selected region
19772  if(!Track->UserGraphicVector.empty()) // skip iteration if empty else have an error
19773  {
19774 
19775  // Isglassen05 (vilhelmgg@gmail.com) reported an error via email and attached an error file on 31/07/20. The error was in the following line which was:
19776 
19777  // for(TTrack::TUserGraphicVector::iterator GraphicPtr = (Track->SelectGraphicVector.end() - 1); GraphicPtr >= Track->SelectGraphicVector.begin();
19778  // GraphicPtr--) // reverse to prevent skipping during erase
19779 
19780  // i.e if the railway included one or more user graphics but the SelectGraphicVector didn't include any, then GraphicPtr wouldn't point to anything and the program would fail
19781  // corrected 01/08/20 by using UserGraphicVector (as it should have been) for SelectGraphicVector. New version v2.4.3.
19782 
19783  for(TTrack::TUserGraphicVector::iterator GraphicPtr = (Track->UserGraphicVector.end() - 1); GraphicPtr >= Track->UserGraphicVector.begin();
19784  GraphicPtr--) // reverse to prevent skipping during erase
19785  {
19786  if((GraphicPtr->HPos >= LowSelectHPos) && ((GraphicPtr->HPos + GraphicPtr->Width) < HighSelectHPos) && (GraphicPtr->VPos >= LowSelectVPos)
19787  && ((GraphicPtr->VPos + GraphicPtr->Height) < HighSelectVPos))
19788  {
19789  for(TTrack::TUserGraphicVector::iterator UserGraphicPtr = (Track->UserGraphicVector.end() - 1);
19790  UserGraphicPtr >= Track->UserGraphicVector.begin(); UserGraphicPtr--) // reverse to prevent skipping during erase
19791  {
19792  if((UserGraphicPtr->HPos == GraphicPtr->HPos) && (UserGraphicPtr->VPos == GraphicPtr->VPos) &&
19793  (UserGraphicPtr->Width == GraphicPtr->Width) && (UserGraphicPtr->Height == GraphicPtr->Height) &&
19794  (UserGraphicPtr->FileName == GraphicPtr->FileName))
19795  {
19796  Track->UserGraphicVector.erase(UserGraphicPtr);
19797  GraphicChangesMade = true;
19798  }
19799  }
19800  }
19801  }
19802  }
19803  // clear the selectvectors
19805  TextHandler->SelectTextVector.clear();
19806  Track->SelectGraphicVector.clear();
19808  Track->CheckMapAndTrack(10); // test
19809  Track->CheckMapAndInactiveTrack(9); // test
19810  Track->CheckLocationNameMultiMap(15); // test
19811  // Track->SetTrackFinished(!NeedToLink); This is an error (see Sam Wainwright email of 24/08/17 & devhistory.txt
19812  // if track not linked to begin with then becomes linked if NeedToLink false
19813  if(NeedToLink)
19814  {
19815  Track->SetTrackFinished(false); // corrected for v2.1.0
19816  }
19817  if(NeedToLink || TextChangesMade || GraphicChangesMade)
19818  {
19819  ResetChangedFileDataAndCaption(21, true); // true for NonPrefDirChangesMade
19820  }
19821  Screen->Cursor = TCursor(-2); // Arrow;
19824  SetLevel1Mode(80);
19826  // No further recursion in AddTrack so OK
19827  UserGraphicReselectPanel->Visible = false;
19828  SetLevel2TrackMode(43);
19829  } break;
19830 
19831  default:
19832  // No further recursion in TrackMode so OK
19833  Track->CopyFlag = false;
19835  SetLevel1Mode(21);
19836  UserGraphicReselectPanel->Visible = false;
19837  break;
19838  }
19839  Utilities->CallLogPop(107);
19840  }
19841  catch (const EClipboardException &e) //non-error catch
19842  {
19843  TrainController->LogEvent("Clipboard error in SetLevel2TrackMode");
19844  Utilities->CallLogPop(2404);
19845  }
19846 }
19847 
19848 // ---------------------------------------------------------------------------
19849 
19851 {
19852  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetLevel2PrefDirMode");
19853  if(Level1Mode != PrefDirMode)
19854  {
19855  // No further recursion in BaseMode so OK
19856  Level1Mode = BaseMode;
19857  SetLevel1Mode(22);
19858  Utilities->CallLogPop(108);
19859  return;
19860  }
19862  {
19863  Utilities->CallLogPop(109);
19864  return;
19865  }
19866  switch(Level2PrefDirMode) // use the data member
19867  {
19868  case PrefDirContinuing:
19869  {
19870  // have to use braces as otherwise the default case bypasses the initialisation of these local variables
19871  InfoPanel->Visible = true;
19872  if(!Display->ZoomOutFlag) // can't set focus if zoomed out, get an error - added this condition for v0.4d
19873  {
19874  AddPrefDirButton->Enabled = true; // this and the line below are to remove focus from any other button that might have it, prior to
19875  AddPrefDirButton->SetFocus(); // disabling the AddPrefDir button, so pressing enter does nothing, it is reset to the AddPrefDir
19876  }
19877  AddPrefDirButton->Enabled = false; // button later if it becomes enabled
19878  DeleteOnePrefDirButton->Enabled = false;
19879  bool LeadingPointsAtLastElement = false;
19880  if(!ConstructPrefDir->EndPossible(0, LeadingPointsAtLastElement))
19881  {
19882  if(LeadingPointsAtLastElement) // size must be > 1
19883  {
19884  InfoPanel->Caption = "PREFERRED DIRECTION SETTING: Can't end on leading points, select next location or truncate";
19885  DeleteOnePrefDirButton->Enabled = true;
19886  }
19887  else // size == 1, DeleteOnePrefDirButton->Enabled remains false
19888  {
19889  InfoPanel->Caption = "PREFERRED DIRECTION SETTING: Select next preferred direction location (right click to truncate)";
19890  }
19891  }
19892  else // size > 1 & EndPossible
19893  {
19894  InfoPanel->Caption = "PREFERRED DIRECTION SETTING: Add selection or select next location (right click to truncate)";
19895  if(!Display->ZoomOutFlag) // can't set focus if zoomed out, get an error - added this condition for v0.4d
19896  {
19897  AddPrefDirButton->Enabled = true;
19898  AddPrefDirButton->SetFocus(); // so can just press 'Enter' key
19899  }
19900  DeleteOnePrefDirButton->Enabled = true;
19901  }
19902  ExitPrefDirButton->Enabled = true;
19903  ClearandRebuildRailway(40); // to show truncated PrefDirs
19904  } break;
19905 
19906  case PrefDirSelecting:
19907  ResetSelectRect(); // so a viewpoint change before a new SelectRect chosen doesn't redisplay the old SelectRect
19908  InfoPanel->Visible = true;
19909  InfoPanel->Caption = "SELECTING: Select area - click left mouse && drag";
19910  SelectMenuItem->Enabled = false;
19911  ReselectMenuItem->Enabled = false;
19912  CancelSelectionMenuItem->Enabled = true;
19913  break;
19914 
19915  default:
19916  // No further recursion in PrefDirMode so OK
19918  SetLevel1Mode(23);
19919  break;
19920  }
19921  Utilities->CallLogPop(110);
19922 }
19923 
19924 // ---------------------------------------------------------------------------
19925 
19927 {
19928  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetLevel2OperMode");
19929  if(Level1Mode != OperMode)
19930  {
19931  // No further recursion in BaseMode so OK
19932  Level1Mode = BaseMode;
19933  SetLevel1Mode(24);
19934  Utilities->CallLogPop(111);
19935  return;
19936  }
19937  if(Level2OperMode == NoOperMode)
19938  {
19939  Utilities->CallLogPop(112);
19940  return;
19941  }
19942  DelayMenu->Visible = true; //these added at v2.13.0
19943  DelayMenu->Enabled = true;
19944  FailureMenu->Visible = true; //these added at v2.14.0
19945  FailureMenu->Enabled = true;
19946  CallingOnButton->Visible = true;
19947  PresetAutoSigRoutesButton->Visible = false;
19948  switch(Level2OperMode) // use the data member
19949  {
19950  case Operating:
19951  {
19952  // have to use braces as otherwise the default case bypasses the initialisation of local variables
19953  OperateButton->Enabled = true;
19954  OperateButton->Glyph->LoadFromResourceName(0, "PauseGraphic");
19955  ExitOperationButton->Enabled = true;
19956  TTClockAdjButton->Enabled = false;
19957  if(TTClockSpeed == 2)
19958  {
19959  TTClockSpeedLabel->Caption = "x2";
19960  }
19961  else if(TTClockSpeed == 4)
19962  {
19963  TTClockSpeedLabel->Caption = "x4";
19964  }
19965  else if(TTClockSpeed == 8)
19966  {
19967  TTClockSpeedLabel->Caption = "x8";
19968  }
19969  else if(TTClockSpeed == 16)
19970  {
19971  TTClockSpeedLabel->Caption = "x16";
19972  }
19973  else if(TTClockSpeed == 0.5)
19974  {
19975  TTClockSpeedLabel->Caption = "x1/2";
19976  }
19977  else if(TTClockSpeed == 0.25)
19978  {
19979  TTClockSpeedLabel->Caption = "x1/4";
19980  }
19981  else if(TTClockSpeed == 0.125)
19982  {
19983  TTClockSpeedLabel->Caption = "x1/8";
19984  }
19985  else if(TTClockSpeed == 0.0625)
19986  {
19987  TTClockSpeedLabel->Caption = "x1/16";
19988  }
19989  else
19990  {
19991  TTClockSpeed = 1;
19992  TTClockSpeedLabel->Caption = "x1";
19993  }
19994  AnsiString TimeMessage = Utilities->Format96HHMMSS(TDateTime(PauseEntryRestartTime)) + ": ";
19996  {
19997  // send message to performance log
19998  if(TTClockSpeed == 2)
19999  {
20000  PerfLogForm->PerformanceLog(6, TimeMessage + "Timetable clock speed changed to twice normal");
20001  }
20002  else if(TTClockSpeed == 4)
20003  {
20004  PerfLogForm->PerformanceLog(7, TimeMessage + "Timetable clock speed changed to four times normal");
20005  }
20006  else if(TTClockSpeed == 8)
20007  {
20008  PerfLogForm->PerformanceLog(8, TimeMessage + "Timetable clock speed changed to eight times normal");
20009  }
20010  else if(TTClockSpeed == 16)
20011  {
20012  PerfLogForm->PerformanceLog(9, TimeMessage + "Timetable clock speed changed to sixteen times normal");
20013  }
20014  else if(TTClockSpeed == 0.5)
20015  {
20016  PerfLogForm->PerformanceLog(10, TimeMessage + "Timetable clock speed changed to half normal");
20017  }
20018  else if(TTClockSpeed == 0.25)
20019  {
20020  PerfLogForm->PerformanceLog(11, TimeMessage + "Timetable clock speed changed to quarter normal");
20021  }
20022  else if(TTClockSpeed == 0.125)
20023  {
20024  PerfLogForm->PerformanceLog(14, TimeMessage + "Timetable clock speed changed to one eighth normal");
20025  }
20026  else if(TTClockSpeed == 0.0625)
20027  {
20028  PerfLogForm->PerformanceLog(15, TimeMessage + "Timetable clock speed changed to one sixteenth normal");
20029  }
20030  else
20031  {
20032  PerfLogForm->PerformanceLog(12, TimeMessage + "Timetable clock speed changed to normal");
20033  }
20034  }
20035  double TTClockTimeChange = double(TrainController->RestartTime) - PauseEntryRestartTime;
20036  if(TTClockTimeChange > 0.000347) // 30 seconds, min increase is 1 minute & don't trust doubles to stay exactly equal
20037  {
20038  // send message to performance log
20039  int MinsIncrease = ((TTClockTimeChange * 1440) + 0.5); // add 30 secs to ensure truncates correctly
20040  int HoursIncrease = 0;
20041  while(MinsIncrease >= 60)
20042  {
20043  HoursIncrease++;
20044  MinsIncrease -= 60;
20045  }
20046  if(HoursIncrease == 0)
20047  {
20048  TimeMessage += "Timetable clock incremented by " + AnsiString(MinsIncrease) + "m";
20049  }
20050  else if(MinsIncrease == 0)
20051  {
20052  TimeMessage += "Timetable clock incremented by " + AnsiString(HoursIncrease) + "h";
20053  }
20054  else
20055  {
20056  TimeMessage += "Timetable clock incremented by " + AnsiString(HoursIncrease) + "h " + AnsiString(MinsIncrease) + "m";
20057  }
20058  PerfLogForm->PerformanceLog(13, TimeMessage);
20059  }
20060  WarningHover = false;
20063  {
20064  MTBFEditBox->Visible = true;
20065  MTBFEditBox->Text = AnsiString(TrainController->AvHoursIntValue);
20066  MTBFEditBox->ReadOnly = true; // because this is not prestart mode
20067  MTBFLabel->Visible = true;
20068  MTBFLabel->Caption = "Mean time between\ntrain failures in\ntimetable hours";
20070  }
20071  else
20072  {
20073  MTBFEditBox->Visible = false;
20074  MTBFEditBox->Text = "";
20075  MTBFLabel->Visible = false;
20076  MTBFLabel->Caption = "Mean time between\ntrain failures in\ntimetable hours";
20078  }
20079  TrainController->BaseTime = TDateTime::CurrentDateTime();
20080 // StopTTClockFlag already false because TTClock stopped by condition "if(!TrainController->StopTTClockFlag && (Level2OperMode == Operating))" in MasterClockTimer function
20081  } break;
20082 
20083  case Paused:
20084  OperateButton->Enabled = true;
20085  OperateButton->Glyph->LoadFromResourceName(0, "RunGraphic");
20086  ExitOperationButton->Enabled = true;
20087  TTClockAdjButton->Enabled = true;
20092 // StopTTClockFlag stays false because TTClock stopped by condition "if(!TrainController->StopTTClockFlag && (Level2OperMode == Operating))" in MasterClockTimer function
20095  break;
20096 
20097  // don't need a separate case for PreStart
20098 
20099  default:
20100  // No further recursion in OperMode so OK
20101  Level1Mode = OperMode;
20102  SetLevel1Mode(25);
20103  break;
20104  }
20105  api_oper_mode_ = int(Level2OperMode); //added at v2.10.0
20106  session_api_->dump(); // update session INI file
20107  Utilities->CallLogPop(113);
20108 }
20109 
20110 // ---------------------------------------------------------------------------
20111 
20112 void TInterface::ApproachLocking(int Caller, TDateTime TTClockTime)
20113 {
20114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ApproachLocking");
20115  float LockDelay = 120.0;
20116 
20117  if(!AllRoutes->LockedRouteVector.empty())
20118  {
20119  for(TAllRoutes::TLockedRouteVectorIterator LRVIT = AllRoutes->LockedRouteVector.end() - 1; LRVIT >= AllRoutes->LockedRouteVector.begin(); LRVIT--)
20120  {
20121  bool BreakFlag = false;
20122  if(AllRoutes->TrackIsInARoute(5, LRVIT->LastTrackVectorPosition, LRVIT->LastXLinkPos))
20123  {
20124  TOneRoute &Route = AllRoutes->GetModifiableRouteAt(0, LRVIT->RouteNumber);
20125  //below added at v2.15.0
20126  TPrefDirElement LastPDElement = Route.GetFixedPrefDirElementAt(265, Route.PrefDirSize() - 1); //these will persist after the actual PDElements have been removed
20127  TPrefDirElement FirstPDElement = Route.GetFixedPrefDirElementAt(266, 0);
20128  //end of section added at v2.15.0
20129  if((TTClockTime - LRVIT->LockStartTime) > TDateTime(LockDelay / 86400))
20130  {
20131  TrainController->LogEvent("LockedRouteRemoved," + AnsiString(LRVIT->RearTrackVectorPosition) + "," +
20132  AnsiString(LRVIT->LastTrackVectorPosition));
20133  while(Route.LastElementPtr(9)->GetTrackVectorPosition() != LRVIT->RearTrackVectorPosition)
20134  {
20135  // examine the element one earlier in the route than the last
20136  if(!(AllRoutes->TrackIsInARoute(6, Route.LastElementPtr(10)->Conn[Route.LastElementPtr(11)->GetELinkPos()],
20137  Route.LastElementPtr(12)->ConnLinkPos[Route.LastElementPtr(13)->GetELinkPos()])))
20138  {
20139  BreakFlag = true;
20140  }
20141  AllRoutes->RemoveRouteElement(1, Route.LastElementPtr(14)->HLoc, Route.LastElementPtr(15)->VLoc, Route.LastElementPtr(16)->GetELink());
20142  if(BreakFlag)
20143  {
20144  break; // train removed earlier element from route so stop here
20145  }
20146  }
20147  if(!BreakFlag)
20148  {
20149  // still need to remove the element at the RearTrackVectorPosition
20150  if(Route.LastElementPtr(17)->GetTrackVectorPosition() == LRVIT->RearTrackVectorPosition)
20151  {
20152  AllRoutes->RemoveRouteElement(2, Route.LastElementPtr(18)->HLoc, Route.LastElementPtr(19)->VLoc,
20153  Route.LastElementPtr(20)->GetELink());
20154  }
20155  }
20156  if(LastPDElement.AutoSignals) //only reclaim sigs for blue routes - added at v2.15.0
20157  {
20158  Route.ReclaimSignalsForNonAutoSigRoutes(2, LastPDElement, FirstPDElement);
20159  }
20160  AllRoutes->CheckMapAndRoutes(10); // test
20161  AllRoutes->LockedRouteVector.erase(LRVIT);
20162  if(!Display->ZoomOutFlag)
20163  {
20164  ClearandRebuildRailway(17); // to get rid of route graphics
20165  }
20167  }
20168  }
20169  else
20170  {
20171  AllRoutes->LockedRouteVector.erase(LRVIT);
20172  // if end element not in route then a train must have entered it from the wrong end and erased the whole route,
20173  // hence no longer needed so get rid of it
20174  }
20175  }
20176  }
20177  Utilities->CallLogPop(743);
20178 }
20179 
20180 // ---------------------------------------------------------------------------
20181 
20182 void TInterface::ContinuationAutoSignals(int Caller, TDateTime TTClockTime) //some changes at v2.17.0
20183 {
20184  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationAutoSignals");
20186  {
20188  for(AutoSigVectorIT = TrainController->ContinuationAutoSigVector.end() - 1; AutoSigVectorIT >= TrainController->ContinuationAutoSigVector.begin();
20189  AutoSigVectorIT--)
20190  {
20191  // Below added at v2.1.0 to prevent locked autosig continuation routes from clearing signals
20192  // need to identify the Continuation element in the route & check if it's in a locked route. If it is then don't call
20193  // SetTrailingSignalsOnContinuationRoute as all signals must stay red.
20194  TPrefDirElement TempPrefDirElement;
20195  int TempLockedVectorNumber;
20196  int LastRouteElement = AllRoutes->GetFixedRouteAt(220, AutoSigVectorIT->RouteNumber).PrefDirSize() - 1;
20197  int TVNum = AllRoutes->GetFixedRouteAt(221, AutoSigVectorIT->RouteNumber).GetFixedPrefDirElementAt(246, LastRouteElement).GetTrackVectorPosition();
20198  // this will be a continuation (error thrown in SetTrailingSignalsOnContinuationRoute if not) & XLinkPos is always 0 for
20199  // route exiting at a continuation
20200  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(14, TVNum, 0, TempPrefDirElement, TempLockedVectorNumber))
20201  {
20202  continue;
20203  }
20204  // end of additions
20205  if(((TTClockTime - AutoSigVectorIT->PassoutTime) > TDateTime(AutoSigVectorIT->FirstDelay / 86400)) && (AutoSigVectorIT->AccessNumber == 0))
20206  {
20207  AutoSigVectorIT->AccessNumber++; //moved to before set signals at v2.17.0 so SetRouteSignals works anytime called as Attribute set to AccessNumber
20208  AllRoutes->SetTrailingSignalsOnContinuationRoute(1, AutoSigVectorIT->RouteNumber, 0);
20209  continue;
20210  }
20211  if(((TTClockTime - AutoSigVectorIT->PassoutTime) > TDateTime(AutoSigVectorIT->SecondDelay / 86400)) && (AutoSigVectorIT->AccessNumber == 1))
20212  {
20213  AutoSigVectorIT->AccessNumber++; //as above
20214  AllRoutes->SetTrailingSignalsOnContinuationRoute(2, AutoSigVectorIT->RouteNumber, 1);
20215  continue;
20216  }
20217  if(((TTClockTime - AutoSigVectorIT->PassoutTime) > TDateTime(AutoSigVectorIT->ThirdDelay / 86400)) && (AutoSigVectorIT->AccessNumber == 2))
20218  {
20219  AutoSigVectorIT->AccessNumber++; //as above
20220  AllRoutes->SetTrailingSignalsOnContinuationRoute(3, AutoSigVectorIT->RouteNumber, 2);
20221  continue;
20222  }
20223  }
20224  // examine all vector for any expired values & erase
20225  for(AutoSigVectorIT = TrainController->ContinuationAutoSigVector.end() - 1; AutoSigVectorIT >= TrainController->ContinuationAutoSigVector.begin();
20226  AutoSigVectorIT--)
20227  {
20228  if(AutoSigVectorIT->AccessNumber > 2)
20229  {
20230  TrainController->ContinuationAutoSigVector.erase(AutoSigVectorIT); // erase expired entries - reverse interation so OK to erase
20231  }
20232 
20233  }
20234  }
20235  Utilities->CallLogPop(744);
20236 }
20237 
20238 // ---------------------------------------------------------------------------
20239 
20241 {
20242  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrackTrainFloat");
20243  TPoint MousePoint = Mouse->CursorPos;
20244  int ScreenX = MousePoint.x - MainScreen->ClientOrigin.x; //mouse position wrt MainScreen
20245  int ScreenY = MousePoint.y - MainScreen->ClientOrigin.y;
20246 
20247  if(SkipTTActionsListBox->Visible ||
20249  ((ScreenX > (MainScreen->Width - 1)) || (ScreenY > (MainScreen->Height - 1)) || (ScreenX < 0) || (ScreenY < 0)))) //button up & mouse outside mainscreen
20250  {
20251  // added !ActionsDueListBoxRightMouseButtonDown at v2.7.0 so can still obtain info & move to trains from ActionsDueListBox even if they are out of the main screen area
20252  // added SkipTTActionsListBox->Visible at v2.11.0 so no floating window when this is displayed
20253  FloatingPanel->Visible = false;
20254  Utilities->CallLogPop(1432);
20255  return;
20256  }
20257 
20258 // mouse in ActionsDueForm if absolute mouseposX >= adfleft && mouseposX <= adfleft + adfwidth && mouseposY >= adftop && mouseposY <= adftop + adfheight
20259  if(ActionsDueForm->Visible && (MousePoint.x >= ActionsDueForm->Left) && (MousePoint.x <= (ActionsDueForm->Left + ActionsDueForm->Width)) &&
20260  (MousePoint.y >= ActionsDueForm->Top) && (MousePoint.y <= (ActionsDueForm->Top + ActionsDueForm->Height)) &&
20262  { //added at v2.13.0 so floating window not shown if mouse inside ActionsDue Form & right button not down in listbox
20263  FloatingPanel->Visible = false;
20264  Utilities->CallLogPop(2240);
20265  return;
20266  }
20267 // mouse in PerfLogForm if absolute mouseposX >= plfleft && mouseposX <= plfleft + plfwidth && mouseposY >= plftop && mouseposY <= plftop + plfheight
20268  if(PerfLogForm->Visible && (MousePoint.x >= PerfLogForm->Left) && (MousePoint.x <= (PerfLogForm->Left + PerfLogForm->Width)) &&
20269  (MousePoint.y >= PerfLogForm->Top) && (MousePoint.y <= (PerfLogForm->Top + PerfLogForm->Height)) &&
20271 // if(PerfLogForm->MouseInClient || PerfLogForm->PerformanceLogBox->MouseInClient)
20272  { //added at v2.13.0 so floating window not shown if mouse inside PerfLogForm & right button not down in ADForm listbox
20273  //(MouseInClient was ok but changed to match ActionsDueForm)
20274  FloatingPanel->Visible = false;
20275  Utilities->CallLogPop(2538);
20276  return;
20277  }
20278  if(TimetableEditPanel->Visible) // added at v2.5.1 as showed track info behind panel
20279  {
20280  if((MousePoint.x >= TimetableEditPanel->Left) && (MousePoint.x <= (TimetableEditPanel->Left + TimetableEditPanel->Width)) &&
20281  ((MousePoint.y - ClientOrigin.y) >= TimetableEditPanel->Top) && ((MousePoint.y - ClientOrigin.y) <=
20282  (TimetableEditPanel->Top + TimetableEditPanel->Height)))
20283  {
20284  // dont show floating window if mouse over TimetableEditPanel
20285  FloatingPanel->Visible = false;
20286  Utilities->CallLogPop(2541);
20287  return;
20288  }
20289  }
20290  AnsiString TrackFloat = "", TrainStatusFloat = "", TrainTTFloat = "";
20291  bool ShowTrackFloatFlag = false, ShowTrainStatusFloatFlag = false, ShowTrainTTFloatFlag = false;
20292  int HLoc, VLoc;
20293 
20294  Track->GetTrackLocsFromScreenPos(4, HLoc, VLoc, ScreenX, ScreenY);
20295 
20296  if(Display->ZoomOutFlag)
20297  {
20298  Utilities->CallLogPop(1123);
20299  return;
20300  }
20301  bool MouseOverOAPanel = false;
20302 // this flag added at v2.7.0 in place of prohibition of all floating windows (which was added at v2.3.0 when Xeon notified me in email of 15/10/19 that they were showing)
20303 //so track info not shown when mouse over OA panel (as now can right click panel to show train info)
20304  if(ActionsDueForm->Visible) //changed to ..Form instead of ..Panel at v2.13.0
20305  {
20306  if((MousePoint.x >= ActionsDueForm->Left) && (MousePoint.x <= (ActionsDueForm->Left + ActionsDueForm->Width)) &&
20307  ((MousePoint.y - ClientOrigin.y) >= ActionsDueForm->Top) && ((MousePoint.y - ClientOrigin.y) <=
20308  (ActionsDueForm->Top + ActionsDueForm->Height)))
20309  {
20310  MouseOverOAPanel = true;
20311  }
20312  }
20313  if((TrackInfoShowing) && !MouseOverOAPanel)
20314  // MouseOverOAPanel condit added at v2.7.0 in place of prohibition of all floating windows (which was added at v2.3.0 when Xeon notified me in email of 15/10/19 that they were showing)
20315  {
20316  bool ActiveTrackFoundFlag = false, InactiveTrackFoundFlag = false, TwoTrack = false;
20317  AnsiString Length01Str = "", Length23Str = "", SpeedLimit01Str = "", SpeedLimit23Str = "";
20318  AnsiString StationEntryStopLinkPos1Str = "", StationEntryStopLinkPos2Str = "";
20319  AnsiString StationEntryStopLinkPos3Str = "", StationEntryStopLinkPos4Str = "";
20320  AnsiString ATrackSN = "", ATrackTN = "", IATrackSN = "", LengthAndSpeedCaption = "";
20321  AnsiString SigAspectString = ""; // new at version 0.6
20322  int ActiveVecPos = Track->GetVectorPositionFromTrackMap(5, HLoc, VLoc, ActiveTrackFoundFlag);
20323  TTrack::TIMPair InactiveVecPositions = Track->GetVectorPositionsFromInactiveTrackMap(3, HLoc, VLoc, InactiveTrackFoundFlag);
20324  TTrackElement ActiveTrackElement, InactiveTrackElement;
20325  if(InactiveTrackFoundFlag)
20326  {
20327  InactiveTrackElement = Track->InactiveTrackElementAt(32, InactiveVecPositions.first); // only need one for the name
20328  IATrackSN = InactiveTrackElement.LocationName;
20329  }
20330  if(ActiveTrackFoundFlag)
20331  {
20332  ActiveTrackElement = Track->TrackElementAt(449, ActiveVecPos);
20333  ATrackSN = ActiveTrackElement.LocationName;
20334  StationEntryStopLinkPos1Str = AnsiString(ActiveTrackElement.StationEntryStopLinkPos1);
20335  StationEntryStopLinkPos2Str = AnsiString(ActiveTrackElement.StationEntryStopLinkPos2);
20336  StationEntryStopLinkPos3Str = AnsiString(ActiveTrackElement.StationEntryStopLinkPos3);
20337  StationEntryStopLinkPos4Str = AnsiString(ActiveTrackElement.StationEntryStopLinkPos4);
20338  ATrackTN = ActiveTrackElement.ActiveTrackElementName;
20339  if((ATrackTN != "") && (!InactiveTrackFoundFlag || ((InactiveTrackElement.TrackType != Platform) &&
20340  (InactiveTrackElement.TrackType != NamedNonStationLocation)) ||
20341  (InactiveTrackElement.LocationName != ActiveTrackElement.ActiveTrackElementName)))
20342  {
20343  ShowMessage("Error - Track has timetable name without corresponding plat/named loc");
20344  }
20345  if(InactiveTrackFoundFlag && ((InactiveTrackElement.TrackType == Platform) || (InactiveTrackElement.TrackType == NamedNonStationLocation)) &&
20346  (InactiveTrackElement.LocationName != ActiveTrackElement.ActiveTrackElementName))
20347  {
20348  ShowMessage("Error - plat/named loc and track have different names, or plat/named loc named but not track");
20349  }
20350  if((ActiveTrackElement.TrackType == Points) || (ActiveTrackElement.TrackType == Bridge) || (ActiveTrackElement.TrackType == Crossover))
20351  {
20352  TwoTrack = true;
20353  }
20354  Length01Str = AnsiString(ActiveTrackElement.Length01);
20355  if(Length01Str == "-1")
20356  {
20357  Length01Str = "Not Set";
20358  }
20359  SpeedLimit01Str = AnsiString(ActiveTrackElement.SpeedLimit01);
20360  if(SpeedLimit01Str == "-1")
20361  {
20362  SpeedLimit01Str = "Not Set";
20363  }
20364  if(TwoTrack)
20365  {
20366  Length23Str = AnsiString(ActiveTrackElement.Length23);
20367  if(Length23Str == "-1")
20368  {
20369  Length23Str = "Not Set"; // shouldn't be -1 but leave in
20370  }
20371  SpeedLimit23Str = AnsiString(ActiveTrackElement.SpeedLimit23);
20372  if(SpeedLimit23Str == "-1")
20373  {
20374  SpeedLimit23Str = "Not Set"; // shouldn't be -1 but leave in
20375  }
20376  if((ActiveTrackElement.TrackType == Points) && (ActiveTrackElement.SpeedTag < 132))
20377  {
20378  LengthAndSpeedCaption = "Straight track length = " + Length01Str + " m" + '\n' + "Diverging track length = " + Length23Str + " m" + '\n' +
20379  "Straight track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Diverging track speed limit = " + SpeedLimit23Str + " km/h";
20380  }
20381  else if(ActiveTrackElement.TrackType == Points)
20382  {
20383  LengthAndSpeedCaption = "Left diverging track length = " + Length01Str + " m" + '\n' + "Right diverging track length = " + Length23Str +
20384  " m" + '\n' + "Left diverging track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Right diverging track Speed Limit = " +
20385  SpeedLimit23Str + " km/h";
20386  }
20387  else if(ActiveTrackElement.TrackType == Crossover)
20388  // crossover links 0 & 1 = diagonal top left to Bottom right, then horizontal, then vertical
20389  {
20390  if((ActiveTrackElement.SpeedTag == 15) || (ActiveTrackElement.SpeedTag == 46))
20391  {
20392  LengthAndSpeedCaption = "Horizontal track length = " + Length01Str + " m" + '\n' + "Other track length = " + Length23Str + " m" + '\n' +
20393  "Horizontal track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Other track speed limit = " + SpeedLimit23Str + " km/h";
20394  }
20395  else if(ActiveTrackElement.SpeedTag == 47)
20396  {
20397  LengthAndSpeedCaption = "Horizontal track length = " + Length23Str + " m" + '\n' + "Other track length = " + Length01Str + " m" + '\n' +
20398  "Horizontal track speed limit = " + SpeedLimit23Str + " km/h" + '\n' + "Other track speed limit = " + SpeedLimit01Str + " km/h";
20399  }
20400  else if(ActiveTrackElement.SpeedTag == 45)
20401  {
20402  LengthAndSpeedCaption = "Vertical track length = " + Length01Str + " m" + '\n' + "Other track length = " + Length23Str + " m" + '\n' +
20403  "Vertical track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Other track speed limit = " + SpeedLimit23Str + " km/h";
20404  }
20405  else if(ActiveTrackElement.SpeedTag == 44)
20406  {
20407  LengthAndSpeedCaption = "Vertical track length = " + Length23Str + " m" + '\n' + "Other track length = " + Length01Str + " m" + '\n' +
20408  "Vertical track speed limit = " + SpeedLimit23Str + " km/h" + '\n' + "Other track speed limit = " + SpeedLimit01Str + " km/h";
20409  }
20410  else if(ActiveTrackElement.SpeedTag == 16)
20411  {
20412  LengthAndSpeedCaption = "Top left to bottom right track length = " + Length01Str + " m" + '\n' + "Other track length = " + Length23Str +
20413  " m" + '\n' + "Top left to bottom right track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Other track speed limit = " +
20414  SpeedLimit23Str + " km/h";
20415  }
20416  }
20417  else // bridge
20418  {
20419  LengthAndSpeedCaption = "Top track length = " + Length01Str + " m" + '\n' + "Bottom track length = " + Length23Str + " m" + '\n' +
20420  "Top track speed limit = " + SpeedLimit01Str + " km/h" + '\n' + "Bottom track speed limit = " + SpeedLimit23Str + " km/h";
20421  }
20422  }
20423  else
20424  {
20425  LengthAndSpeedCaption = "Track length = " + Length01Str + " m" + '\n' + "Track speed limit = " + SpeedLimit01Str + " km/h";
20426  }
20427  }
20428  if(ActiveTrackFoundFlag)
20429  {
20430  // note that now the "In timetable..." line removed much of the below could be simplified, but leave as is
20431  // in case wish to resurrect this line for any reason
20432  ShowTrackFloatFlag = true;
20433  if(ATrackTN != "") // has a timetable name & therefore has a valid platform or non-station name
20434  {
20435  TrackFloat = "Location = " + ATrackTN + '\n' + LengthAndSpeedCaption + '\n' + "ID = " + AnsiString(ActiveTrackElement.ElementID);
20436  }
20437  else if(ATrackSN != "") // no timetable name but location name, i.e. a footcrossing
20438  {
20439  TrackFloat = "Location = " + ATrackSN + '\n' + LengthAndSpeedCaption + '\n' + "ID = " + AnsiString(ActiveTrackElement.ElementID);
20440  }
20441  else if(InactiveTrackFoundFlag) // no timetable name yet but unnamed inactive element at same location (can't be a parapet if active element there)
20442  {
20443  TrackFloat = "Location unnamed\n" + LengthAndSpeedCaption + '\n' + "ID = " + AnsiString(ActiveTrackElement.ElementID);
20444  }
20445  else // no timetable or location name, just track
20446  {
20447  TrackFloat = LengthAndSpeedCaption + '\n' + "Track Element ID = " + AnsiString(ActiveTrackElement.ElementID);
20448  }
20449  if(ActiveTrackElement.TrackType == SignalPost) // new for version 0.6
20450  {
20451  if(ActiveTrackElement.SigAspect == TTrackElement::ThreeAspect)
20452  {
20453  SigAspectString = "\nThree-aspect signal";
20454  }
20455  else if(ActiveTrackElement.SigAspect == TTrackElement::TwoAspect)
20456  {
20457  SigAspectString = "\nTwo-aspect signal";
20458  }
20459  else if(ActiveTrackElement.SigAspect == TTrackElement::GroundSignal)
20460  {
20461  SigAspectString = "\nGround signal";
20462  }
20463  else
20464  {
20465  SigAspectString = "\nFour-aspect signal";
20466  }
20467  TrackFloat += SigAspectString;
20468  }
20469 //failed points info added at v2.13.0
20470  if((ActiveTrackElement.TrackType == Points) && ActiveTrackElement.Failed) //failed points
20471  {
20472  AnsiString RepairTimeStr;
20473  TDateTime Now = TrainController->TTClockTime;
20474  for(TTrack::TFailedElementVector::iterator FPVIt = Track->FailedPointsVector.begin(); FPVIt != Track->FailedPointsVector.end(); FPVIt++)
20475  {
20476  if(ActiveVecPos == FPVIt->TVPos) //found the failed point
20477  {
20478  if((Now - FPVIt->FailureTime) <= TDateTime(Utilities->RepairDiagnosisTime/1440)) //less than 5 minutes since failed - type of failure not yet identified
20479  {
20480  RepairTimeStr = "not yet known.";
20481  }
20482  else if((FPVIt->RepairTime - Now) <= TDateTime(10.0/1440))
20483  {
20484  RepairTimeStr = "only a few minutes.";
20485  }
20486  else if((FPVIt->RepairTime - Now) <= TDateTime(30.0/1440))
20487  {
20488  RepairTimeStr = "30 minutes or less.";
20489  }
20490  else if((FPVIt->RepairTime - Now) <= TDateTime(60.0/1440))
20491  {
20492  RepairTimeStr = "30 minutes to an hour.";
20493  }
20494  else if((FPVIt->RepairTime - Now) <= TDateTime(120.0/1440))
20495  {
20496  RepairTimeStr = "1 to 2 hours.";
20497  }
20498  else if((FPVIt->RepairTime - Now) > TDateTime(120.0/1440))
20499  {
20500  RepairTimeStr = "over 2 hours.";
20501  }
20502  break; //no point looking further
20503  }
20504  }
20505  TrackFloat += "\nPoints failed. Repair time " + RepairTimeStr;
20506  }
20507 //failed signals info added at v2.13.0
20508  if((ActiveTrackElement.TrackType == SignalPost) && ActiveTrackElement.Failed) //failed signals
20509  {
20510  AnsiString RepairTimeStr;
20511  TDateTime Now = TrainController->TTClockTime;
20512  for(TTrack::TFailedElementVector::iterator FPVIt = Track->FailedSignalsVector.begin(); FPVIt != Track->FailedSignalsVector.end(); FPVIt++)
20513  {
20514  if(ActiveVecPos == FPVIt->TVPos) //found the failed signal
20515  {
20516  if((Now - FPVIt->FailureTime) <= TDateTime(Utilities->RepairDiagnosisTime/1440)) //less than 5 minutes since failed - type of failure not yet identified
20517  {
20518  RepairTimeStr = "not yet known.";
20519  }
20520  else if((FPVIt->RepairTime - Now) <= TDateTime(10.0/1440))
20521  {
20522  RepairTimeStr = "only a few minutes.";
20523  }
20524  else if((FPVIt->RepairTime - Now) <= TDateTime(30.0/1440))
20525  {
20526  RepairTimeStr = "30 minutes or less.";
20527  }
20528  else if((FPVIt->RepairTime - Now) <= TDateTime(60.0/1440))
20529  {
20530  RepairTimeStr = "30 minutes to an hour.";
20531  }
20532  else if((FPVIt->RepairTime - Now) <= TDateTime(120.0/1440))
20533  {
20534  RepairTimeStr = "1 to 2 hours.";
20535  }
20536  else if((FPVIt->RepairTime - Now) > TDateTime(120.0/1440))
20537  {
20538  RepairTimeStr = "over 2 hours.";
20539  }
20540  break; //no point looking further
20541  }
20542  }
20543  TrackFloat += "\nSignal failed. Repair time " + RepairTimeStr;
20544  }
20545 //TSR info added at v2.13.0
20546  if((ActiveTrackElement.TrackType == Simple) && ActiveTrackElement.Failed) //TSR
20547  {
20548  AnsiString RepairTimeStr;
20549  TDateTime Now = TrainController->TTClockTime;
20550  for(TTrack::TFailedElementVector::iterator FPVIt = Track->TSRVector.begin(); FPVIt != Track->TSRVector.end(); FPVIt++)
20551  {
20552  if(ActiveVecPos == FPVIt->TVPos) //found the TSR
20553  {
20554  if((Now - FPVIt->FailureTime) <= TDateTime(Utilities->RepairDiagnosisTime/1440)) //less than 5 minutes since failed - type of failure not yet identified
20555  {
20556  RepairTimeStr = "not yet known.";
20557  }
20558  else if((FPVIt->RepairTime - Now) <= TDateTime(10.0/1440))
20559  {
20560  RepairTimeStr = "only a few minutes.";
20561  }
20562  else if((FPVIt->RepairTime - Now) <= TDateTime(30.0/1440))
20563  {
20564  RepairTimeStr = "30 minutes or less.";
20565  }
20566  else if((FPVIt->RepairTime - Now) <= TDateTime(60.0/1440))
20567  {
20568  RepairTimeStr = "30 minutes to an hour.";
20569  }
20570  else if((FPVIt->RepairTime - Now) <= TDateTime(120.0/1440))
20571  {
20572  RepairTimeStr = "1 to 2 hours.";
20573  }
20574  else if((FPVIt->RepairTime - Now) > TDateTime(120.0/1440))
20575  {
20576  RepairTimeStr = "over 2 hours.";
20577  }
20578  break; //no point looking further
20579  }
20580  }
20581  TrackFloat += "\nTemporary Speed Restriction in place. Restoration time " + RepairTimeStr;
20582  }
20583  } // if(ActiveFoundFlag)
20584  else if(InactiveTrackFoundFlag) // inactive element but no active element,
20585  // i.e. concourse or non-station name at a blank element
20586  {
20587  ShowTrackFloatFlag = true;
20588  if(InactiveTrackElement.TrackType != Parapet)
20589  {
20590  if(IATrackSN == "")
20591  {
20592  TrackFloat = "Location unnamed\nID = " + AnsiString(InactiveTrackElement.ElementID);
20593  }
20594  else
20595  {
20596  TrackFloat = "Location = " + IATrackSN + '\n' + "ID = " + AnsiString(InactiveTrackElement.ElementID);
20597  }
20598  }
20599  else // it is a parapet, just show the ID
20600  {
20601  TrackFloat = "ID = " + AnsiString(InactiveTrackElement.ElementID);
20602  }
20603  }
20604  }
20605 // end of TrackFloat section
20606 
20607  bool ActionsDueListBoxFloatRequired = false; // identifies which window needs the float
20609  // if caption is 'Hide' label is required
20610  {
20611  bool FoundFlag;
20612  AnsiString FormatOneDPStr = "####0.0";
20613  AnsiString FormatNoDPStr = "#######0";
20614  AnsiString MaxBrakeStr = "";
20615  AnsiString SpecialStr = "";
20616  if(ActionsDueForm->Visible) // added at v2.6.2 to show floating window for trains in actions due list
20617  {
20619  {
20620  int X = ActionsDueForm->ActionsDueListBox->ScreenToClient(MousePoint).x;
20621  int Y = ActionsDueForm->ActionsDueListBox->ScreenToClient(MousePoint).y;
20622  int TrainID = -1, ContinuationPos = -1;
20623  if(GetTrainIDOrContinuationPosition(1, X, Y, TrainID, ContinuationPos))
20624  {
20625  ActionsDueListBoxFloatRequired = true;
20626  if(TrainStatusShowing)
20627  {
20628  ShowTrainStatusFloatFlag = true;
20629  }
20630  if(TrainTTShowing)
20631  {
20632  ShowTrainTTFloatFlag = true;
20633  }
20634  if((TrainID > -1) && (ShowTrainStatusFloatFlag || ShowTrainTTFloatFlag))
20635  {
20636  TTrain Train = TrainController->TrainVectorAtIdent(53, TrainID);
20637  TrainStatusFloat = GetTrainStatusFloat(0, TrainID, FormatNoDPStr, SpecialStr);
20638  TrainTTFloat = Train.FloatingTimetableString(1, Train.ActionVectorEntryPtr);
20639  }
20640  else if(ContinuationPos > -1)
20641  {
20642  GetTrainFloatingInfoFromContinuation(0, ContinuationPos, FormatNoDPStr, SpecialStr, TrainStatusFloat, TrainTTFloat);
20643  }
20644  }
20645  }
20646  }
20647  if(!ActionsDueListBoxFloatRequired) // condition added at v2.6.2 so only one floating window can show
20648  {
20649  int VecPos = Track->GetVectorPositionFromTrackMap(6, HLoc, VLoc, FoundFlag);
20650  if(FoundFlag && !MouseOverOAPanel) // MouseOverOAPanel added at v2.7.0 to prevent trains showimng behind OA panel
20651  {
20652  if(Track->TrackElementAt(450, VecPos).TrainIDOnElement > -1)
20653  // if a bridge & 2 trains at that position will select the train with TrainIDOnElement set
20654  {
20655  int TrainID = Track->TrackElementAt(452, VecPos).TrainIDOnElement;
20656  if(TrainStatusShowing)
20657  {
20658  ShowTrainStatusFloatFlag = true;
20659  TrainStatusFloat = GetTrainStatusFloat(1, TrainID, FormatNoDPStr, SpecialStr);
20660  }
20661  if(TrainTTShowing)
20662  {
20663  ShowTrainTTFloatFlag = true;
20664  TTrain Train = TrainController->TrainVectorAtIdent(54, TrainID);
20665  TrainTTFloat = Train.FloatingTimetableString(0, Train.ActionVectorEntryPtr);
20666  }
20667  }
20668 
20669  else if(Track->TrackElementAt(666, VecPos).TrackType == Continuation)
20670  // always give train information if a train present, but if not & either of train status or timetable info
20671  // selected then give next expected train to enter, or 'No trains expected'
20672  {
20673  TrainStatusFloat = "No trains expected";
20674  TrainTTFloat = "No timetable";
20675  if(TrainStatusShowing)
20676  {
20677  ShowTrainStatusFloatFlag = true;
20678  }
20679  if(TrainTTShowing)
20680  {
20681  ShowTrainTTFloatFlag = true;
20682  }
20684  {
20685  GetTrainFloatingInfoFromContinuation(1, VecPos, FormatNoDPStr, SpecialStr, TrainStatusFloat, TrainTTFloat);
20686  }
20687  }
20688  }
20689  }
20690  }
20691 // end of TrainFloat section
20692  AnsiString Caption;
20693 
20694  if(!ShowTrackFloatFlag && !ShowTrainStatusFloatFlag && !ShowTrainTTFloatFlag)
20695  {
20696  FloatingPanel->Visible = false;
20697  Utilities->CallLogPop(1485);
20698  return; // return with label invisible
20699  }
20700  else if(ShowTrackFloatFlag && !ShowTrainStatusFloatFlag && !ShowTrainTTFloatFlag)
20701  {
20702  Caption = TrackFloat;
20703  }
20704  else if(!ShowTrackFloatFlag && ShowTrainStatusFloatFlag && !ShowTrainTTFloatFlag)
20705  {
20706  Caption = TrainStatusFloat;
20707  }
20708  else if(ShowTrackFloatFlag && ShowTrainStatusFloatFlag && !ShowTrainTTFloatFlag)
20709  {
20710  Caption = TrainStatusFloat + '\n' + '\n' + TrackFloat;
20711  }
20712  else if(!ShowTrackFloatFlag && !ShowTrainStatusFloatFlag && ShowTrainTTFloatFlag)
20713  {
20714  if(TrainStatusFloat == "No trains expected")
20715  {
20716  Caption = TrainStatusFloat;
20717  }
20718  else
20719  {
20720  Caption = TrainTTFloat;
20721  }
20722  }
20723  else if(ShowTrackFloatFlag && !ShowTrainStatusFloatFlag && ShowTrainTTFloatFlag)
20724  {
20725  if(TrainStatusFloat == "No trains expected")
20726  {
20727  Caption = TrainStatusFloat + '\n' + '\n' + TrackFloat;
20728  }
20729  else
20730  {
20731  Caption = TrainTTFloat + '\n' + '\n' + TrackFloat;
20732  }
20733  }
20734  else if(!ShowTrackFloatFlag && ShowTrainStatusFloatFlag && ShowTrainTTFloatFlag)
20735  {
20736  if(TrainStatusFloat == "No trains expected")
20737  {
20738  Caption = TrainStatusFloat;
20739  }
20740  else
20741  {
20742  Caption = TrainStatusFloat + '\n' + '\n' + TrainTTFloat;
20743  }
20744  }
20745  else if(ShowTrackFloatFlag && ShowTrainStatusFloatFlag && ShowTrainTTFloatFlag)
20746  {
20747  if(TrainStatusFloat == "No trains expected")
20748  {
20749  Caption = TrainStatusFloat + '\n' + '\n' + TrackFloat;
20750  }
20751  else
20752  {
20753  Caption = TrainStatusFloat + '\n' + '\n' + TrainTTFloat + '\n' + '\n' + TrackFloat;
20754  }
20755  }
20756  int WindowOffsetLeft = 16;
20757  int WindowOffsetRight = 16;
20758  if(ActionsDueListBoxFloatRequired)
20759  {
20760  if((ActionsDueForm->Left >= (Interface->Left + Interface->Width)) || ((ActionsDueForm->Left + ActionsDueForm->Width) <= Interface->Left) ||
20761  (ActionsDueForm->Top >= (Interface->Top + Interface->Height)) || ((ActionsDueForm->Top + ActionsDueForm->Height) <= Interface->Top))
20762  //AD form outside interface so floating window in TRH corner of mainscreen
20763  {
20764  FloatingLabel->Caption = Caption; //added here as return in this function, see below for information
20765  FloatingPanel->Visible = true;
20766  FloatingPanel->Left = MainScreen->Left + MainScreen->Width - FloatingPanel->Width;
20767  FloatingPanel->Top = MainScreen->Top;
20768  FloatingPanel->BringToFront();
20769  Utilities->CallLogPop(2544);
20770  return;
20771  }
20772  else
20773  {
20774  WindowOffsetLeft = 96; //was 32;
20775  WindowOffsetRight = 96; //was 64;
20776  }
20777  }
20778  FloatingLabel->Caption = Caption; // set this here so dimensions correct in calculations, moved from below at v2.7.0
20779  FloatingPanel->Visible = true; // need this or dimensions still not valid, moved from below at v2.7.0
20780 
20781  int Left = ScreenX + MainScreen->Left + WindowOffsetRight; // so lhs of window is WindowOffset to the right of the mouse pos
20782 // this offset is because window position is relative to the interface form, whereas ScreenX & Y are relative to the MainScreen, which is
20783 // offset 32 to the right and 95 down from the interface form
20784  if((Left + FloatingPanel->Width) > MainScreen->Left + MainScreen->Width)
20785  {
20786  Left = ScreenX - FloatingPanel->Width + 32 - WindowOffsetLeft;
20787  }
20788 // so rhs of window is 32 - WindowOffset to the left of the mouse pos (+32 would be at mouse pos)
20789  int Top = ScreenY + MainScreen->Top + 16; // so top of window is one element below the mouse pos (ScreenY + MainScreen->Top would be at mouse pos)
20790 
20791  if((Top + FloatingPanel->Height) > MainScreen->Top + MainScreen->Height)
20792  {
20793  Top = ScreenY - FloatingPanel->Height + 79; // so bottom of window is one element above the mouse pos (95 would be at mouse pos)
20794  // but, top may now be off the top of the screen, if so position at the top of the screen, as always need to see the top, if have to
20795  // lose something then it's best to be from the bottom
20796  if(Top < 30) // use 30 instead of MainScreen->Top [95] as top can go off MainScreen providing it doesn't reach the information panel, as that would
20797  // obscure the window
20798  {
20799  Top = 30;
20800  }
20801  }
20802 /* if((Left != FloatingPanel->Left) || (Top != FloatingPanel->Top)) //dropped at v2.7.0 as causes more flicker than allowing window to move with mouse
20803  {
20804  FloatingPanel->Visible = false; // so doesn't flicker when reposition
20805  FloatingPanel->Left = Left;
20806  FloatingPanel->Top = Top;
20807  Utilities->CallLogPop(1917);
20808  return;
20809  }
20810 */
20811 
20812  FloatingPanel->Left = Left; // new at v2.7.0 in place of above
20813  FloatingPanel->Top = Top;
20814 
20815 // FloatingLabel->Caption = Caption; moved up at v2.7.0
20816 // FloatingPanel->Visible = true; // moved up at v2.7.0
20817  FloatingPanel->BringToFront();
20818  Utilities->CallLogPop(746);
20819 }
20820 
20821 // ---------------------------------------------------------------------------
20822 
20823 void TInterface::GetTrainFloatingInfoFromContinuation(int Caller, int VecPos, AnsiString FormatNoDPStr, AnsiString SpecialStr, AnsiString &TrainStatusFloat,
20824  AnsiString &TrainTTFloat)
20825 {
20826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainFloatingInfoFromContinuation");
20828  int LineSpeedLimit = Track->TrackElementAt(906, VecPos).SpeedLimit01; // speed only in 01 as a continuation
20829  float EntrySpeed;
20831  {
20832  while((CTEIt != TrainController->ContinuationTrainExpectationMultiMap.end()) && ((CTEIt->second.VectorPosition != VecPos) ||
20833  (CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)))
20834  {
20835  CTEIt++;
20836  }
20838  {
20839  TTrainDataEntry *TTDEPtr = CTEIt->second.TrainDataEntryPtr;
20840  AnsiString ServiceReferenceInfo = "";
20841  // Repeat information
20842  if(TTDEPtr->NumberOfTrains > 1) // Service reference information
20843  {
20844  if(CTEIt->second.RepeatNumber == 0)
20845  {
20846  if(CTEIt->second.HeadCode != TTDEPtr->ServiceReference)
20847  {
20848  ServiceReferenceInfo = "\nFirst service of ref. " + TTDEPtr->ServiceReference;
20849  }
20850  else
20851  {
20852  ServiceReferenceInfo = "\nFirst service";
20853  }
20854  }
20855  else if(CTEIt->second.HeadCode == TTDEPtr->ServiceReference)
20856  {
20857  ServiceReferenceInfo = "\nRepeat service no. " + AnsiString(CTEIt->second.RepeatNumber);
20858  }
20859  else
20860  {
20861  ServiceReferenceInfo = "\nRepeat service no. " + AnsiString(CTEIt->second.RepeatNumber) + " of ref. " + TTDEPtr->ServiceReference;
20862  }
20863  }
20864  else
20865  {
20866  if(CTEIt->second.HeadCode != TTDEPtr->ServiceReference)
20867  {
20868  ServiceReferenceInfo = "\nService reference " + TTDEPtr->ServiceReference;
20869  }
20870  }
20871  if(TTDEPtr->ActionVector.at(0).SignallerControl) // entry at 0 is the start entry
20872  {
20873  SpecialStr = "\nTrain under signaller control";
20874  EntrySpeed = TTDEPtr->SignallerSpeed;
20875  if(EntrySpeed > LineSpeedLimit)
20876  {
20877  EntrySpeed = LineSpeedLimit;
20878  }
20879  }
20880  else
20881  {
20882  EntrySpeed = TTDEPtr->StartSpeed;
20883  if(EntrySpeed > LineSpeedLimit)
20884  {
20885  EntrySpeed = LineSpeedLimit;
20886  }
20887  }
20888  if((CTEIt->first + TDateTime(1.0 / 1440)) < TrainController->TTClockTime) // has to be at least 1 min late to show as late
20889  {
20890  TDateTime TempTime = CTEIt->first;
20891 // need this because CTEIt points to a const object and shouldn't use FormatString on a const object
20892  TrainStatusFloat = CTEIt->second.HeadCode + ": " + CTEIt->second.FixedDescription + ServiceReferenceInfo + "\nEntry speed " +
20893  AnsiString::FormatFloat(FormatNoDPStr, EntrySpeed) + "km/h" + SpecialStr + "\nDelayed, was due at " + Utilities->Format96HHMM(TempTime); //FixedDescription = new name, changed at v2.16.1
20894  }
20895  else
20896  {
20897  TDateTime TempTime = CTEIt->first;
20898 // need this because CTEIt points to a const object and shouldn't use FormatString on a const object
20899  TrainStatusFloat = CTEIt->second.HeadCode + ": " + CTEIt->second.FixedDescription + ServiceReferenceInfo + "\nEntry speed " +
20900  AnsiString::FormatFloat(FormatNoDPStr, EntrySpeed) + "km/h" + SpecialStr + "\nExpected at " + Utilities->Format96HHMM(TempTime); //FixedDescription = new name, changed at v2.16.1
20901  }
20902  if(TrainTTInfoOnOffMenuItem->Caption == "Hide Train &Timetable Information") //at v2.22.0 this was left as Hide Timetable so failed to show timetable after caption updated
20903  { //corrected at v2.22.1
20904  if(!TTDEPtr->ActionVector.at(0).SignallerControl) // if signaller control there's no timetable & SpecialStr covers this
20905  {
20906  TrainTTFloat = TrainController->ContinuationEntryFloatingTTString(0, TTDEPtr, CTEIt->second.RepeatNumber, CTEIt->second.IncrementalMinutes,
20907  CTEIt->second.IncrementalDigits);
20908  }
20909  }
20910  }
20911  }
20912  Utilities->CallLogPop(2262);
20913 }
20914 
20915 // ---------------------------------------------------------------------------
20916 
20917 AnsiString TInterface::GetTrainStatusFloat(int Caller, int TrainID, AnsiString FormatNoDPStr, AnsiString SpecialStr)
20918 // new at v2.6.2 to make it easier to show also from actions due panel
20919 {
20920  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainStatusFloat");
20921  AnsiString HeadCode = "", ServiceReferenceInfo = "", Status = "", CurrSpeedStr = "", BrakePCStr = "", NextStopStr = "", TimeLeftStr = "",
20922  TimeToNextMovementStr = "", MassStr = "", PowerStr = "";
20923  AnsiString FormatOneDPStr = "####0.0", MaxBrakeStr = "", MaxSpeedStr = "", MaxMPHStr = "", TrainStatusFloat; //MaxMPHStr added at v2.15.0
20924 
20925  double CurrSpeed;
20926  TTrain Train = TrainController->TrainVectorAtIdent(1, TrainID);
20927  MassStr = AnsiString::FormatFloat(FormatNoDPStr, ((double)Train.Mass) / 1000); // Te
20928  PowerStr = AnsiString::FormatFloat(FormatNoDPStr, Train.PowerAtRail / 1000 / 0.8); // kW
20929  if(Train.BeingCalledOn)
20930  {
20931  MaxSpeedStr = "30";
20932  MaxMPHStr = "19mph";
20933  }
20934  else
20935  {
20936  MaxSpeedStr = AnsiString::FormatFloat(FormatNoDPStr, Train.MaxRunningSpeed);
20937  MaxMPHStr = AnsiString::FormatFloat(FormatNoDPStr, (Train.MaxRunningSpeed * 5 / 8)) + "mph";
20938  }
20939  TDateTime ElapsedDeltaT = TrainController->TTClockTime - Train.EntryTime;
20940  TDateTime FirstHalfTimeDeltaT = Train.ExitTimeHalf - Train.EntryTime;
20941  TDateTime SecondHalfTimeDeltaT = Train.ExitTimeFull - Train.EntryTime - FirstHalfTimeDeltaT;
20942  TDateTime TimeLeft;
20943  double BrakePCRate = Train.BrakeRate * 100.0 / Train.MaxBrakeRate;
20944  MaxBrakeStr = AnsiString::FormatFloat(FormatNoDPStr, (Train.MaxBrakeRate * Train.Mass / 9810));
20945  HeadCode = Train.HeadCode;
20946  if(Train.TrainDataEntryPtr->NumberOfTrains > 1) // Service reference information added at v0.6b
20947  {
20948  if(Train.RepeatNumber == 0)
20949  {
20950  if(HeadCode != Train.TrainDataEntryPtr->ServiceReference)
20951  {
20952  ServiceReferenceInfo = "\nFirst service of ref. " + Train.TrainDataEntryPtr->ServiceReference;
20953  }
20954  else
20955  {
20956  ServiceReferenceInfo = "\nFirst service";
20957  }
20958  }
20959  else if(HeadCode == Train.TrainDataEntryPtr->ServiceReference)
20960  {
20961  ServiceReferenceInfo = "\nRepeat service no. " + AnsiString(Train.RepeatNumber);
20962  }
20963  else
20964  {
20965  ServiceReferenceInfo = "\nRepeat service no. " + AnsiString(Train.RepeatNumber) + " of ref. " + Train.TrainDataEntryPtr->ServiceReference;
20966  }
20967  }
20968  else
20969  {
20970  if(HeadCode != Train.TrainDataEntryPtr->ServiceReference)
20971  {
20972  ServiceReferenceInfo = "\nService reference " + Train.TrainDataEntryPtr->ServiceReference;
20973  }
20974  }
20975  if(Train.Stopped())
20976  {
20977  if(Train.SignallerStopped)
20978  {
20979  Status = "Stopped on signaller's instruction"; // if stopped for any other reason that will diplay
20980  }
20981  if(Train.NotInService)
20982  {
20983  Status = "Not in service"; // not used so far but leave it in
20984  }
20985  if(Train.StoppedAtBuffers)
20986  {
20987  Status = "Stopped at buffers";
20988  }
20989  if(Train.StoppedAtSignal)
20990  {
20991  Status = "Stopped at signal";
20992  }
20993  if((Train.TrainInFront) || (Train.StoppedForTrainInFront))
20994  {
20995  Status = "Stopped - forward track occupied"; // before station stop as want to display station stop if that set
20996  }
20997  if(Train.RevisedStoppedAtLoc())
20998  {
20999  Status = "Stopped at " + Train.ActionVectorEntryPtr->LocationName;
21000  }
21001  if((Train.RevisedStoppedAtLoc()) && (Train.TrainInFront))
21002  {
21003  Status = "Stopped at " + Train.ActionVectorEntryPtr->LocationName + " + forward track occupied";
21004  }
21005  if(Train.StoppedWithoutPower)
21006  {
21007  if(Train.TrainFailed)
21008  {
21009  Status = "Stopped without power - train failed";
21010  }
21011  else
21012  {
21013  Status = "Stopped without power";
21014  }
21015  }
21016  if(Train.StoppedAfterSPAD)
21017  {
21018  Status = "Stopped - signal passed at danger";
21019  }
21020  if(Train.Derailed)
21021  {
21022  Status = "Derailed";
21023  }
21024  if(Train.Crashed)
21025  {
21026  Status = "Crashed";
21027  }
21028  CurrSpeed = 0;
21029  }
21030  else if(Train.OneLengthAccelDecel)
21031  {
21032  if(Train.FirstHalfMove)
21033  {
21034  Status = "Accelerating"; // just display a linear speed rise over half length
21035  BrakePCRate = 0; // reset to proper value during braking
21036  CurrSpeed = Train.EntrySpeed + ((Train.ExitSpeedHalf - Train.EntrySpeed) * (double(ElapsedDeltaT) / double(FirstHalfTimeDeltaT)));
21037  }
21038  else
21039  {
21040  BrakePCRate = Train.BrakeRate * 100.0 / Train.MaxBrakeRate;
21041  if(BrakePCRate < 55)
21042  {
21043  Status = "Light braking";
21044  }
21045  else if(BrakePCRate < 90)
21046  {
21047  Status = "Heavy braking";
21048  }
21049  else
21050  {
21051  Status = "Emergency braking";
21052  }
21053  CurrSpeed = Train.ExitSpeedHalf - 3.6 * (Train.BrakeRate * double(TrainController->TTClockTime - Train.ExitTimeHalf) * 86400.0);
21054  }
21055  }
21056  else if(Train.BrakeRate > Train.CoastingBrakeRate)
21057  {
21058  if(BrakePCRate < 55)
21059  {
21060  Status = "Light braking";
21061  }
21062  else if(BrakePCRate < 90)
21063  {
21064  Status = "Heavy braking";
21065  }
21066  else
21067  {
21068  Status = "Emergency braking";
21069  }
21070  CurrSpeed = Train.EntrySpeed - 3.6 * (Train.BrakeRate * double(ElapsedDeltaT) * 86400.0);
21071  }
21072 
21073  else if((Train.BrakeRate <= 0.01) && (Train.ExitSpeedHalf > (Train.EntrySpeed + 0.01)) && Train.FirstHalfMove)
21074  {
21075  Status = "Accelerating"; // just display a linear speed rise over half length
21076  CurrSpeed = Train.EntrySpeed + ((Train.ExitSpeedHalf - Train.EntrySpeed) * (double(ElapsedDeltaT) / double(FirstHalfTimeDeltaT)));
21077  }
21078 
21079  else if((Train.BrakeRate <= 0.01) && (Train.ExitSpeedFull > (Train.ExitSpeedHalf + 0.01)) && !Train.FirstHalfMove)
21080  {
21081  Status = "Accelerating";
21082  CurrSpeed = Train.ExitSpeedHalf +
21083  ((Train.ExitSpeedFull - Train.ExitSpeedHalf) * (double(ElapsedDeltaT - FirstHalfTimeDeltaT) / double(SecondHalfTimeDeltaT)));
21084  }
21085 
21086  else if((Train.BrakeRate <= Train.CoastingBrakeRate) && (Train.ExitSpeedFull <= Train.ExitSpeedHalf) && !Train.FirstHalfMove)
21087  {
21088  CurrSpeed = Train.ExitSpeedFull;
21089  if(Train.PowerAtRail < 1) // as designed there is no way a vehicle can coast without having failed
21090  {
21091  if(Train.TrainFailed)
21092  {
21093  Status = "Coasting - train failed";
21094  }
21095  else
21096  {
21097  Status = "Coasting - no power";
21098  }
21099  }
21100  else
21101  {
21102  Status = "Constant speed";
21103  }
21104  }
21105 
21106  else if((Train.BrakeRate <= Train.CoastingBrakeRate) && (Train.ExitSpeedFull <= Train.ExitSpeedHalf) && Train.FirstHalfMove)
21107  {
21108  CurrSpeed = Train.ExitSpeedHalf;
21109  if(Train.PowerAtRail < 1) // as designed there is no way a vehicle can coast without having failed
21110  {
21111  if(Train.TrainFailed)
21112  {
21113  Status = "Coasting - train failed";
21114  }
21115  else
21116  {
21117  Status = "Coasting - no power";
21118  }
21119  }
21120  else
21121  {
21122  Status = "Constant speed";
21123  }
21124  }
21125  if(Train.TimetableFinished)
21126  {
21127  if(Train.TrainMode == Signaller)
21128  {
21129  NextStopStr = "At signaller's discretion";
21130  }
21131  else
21132  {
21133  NextStopStr = "None";
21134  }
21135  }
21136  else
21137  {
21138  NextStopStr = Train.FloatingLabelNextString(0, Train.ActionVectorEntryPtr);
21139  }
21140  if(Train.TrainMode == Signaller)
21141  {
21142  SpecialStr = "Train under signaller control" + AnsiString('\n');
21143  }
21144  else if(Train.BeingCalledOn && !Train.RevisedStoppedAtLoc())
21145  {
21146  SpecialStr = "Restricted speed - being called on" + AnsiString('\n');
21147  }
21148  double RemTimeHalf = 86400.0 * double(Train.ExitTimeHalf - TrainController->TTClockTime);
21149  if(RemTimeHalf < 0)
21150  {
21151  RemTimeHalf = 0;
21152  }
21153  double RemTimeFull = 86400.0 * double(Train.ExitTimeFull - TrainController->TTClockTime);
21154  if(RemTimeFull < 0)
21155  {
21156  RemTimeFull = 0;
21157  }
21158  if(RemTimeHalf > 0)
21159  {
21160  TimeLeft = RemTimeHalf;
21161  }
21162  else
21163  {
21164  TimeLeft = RemTimeFull;
21165  }
21166  TimeToNextMovementStr = "Time to next movement (sec) = " + TimeLeftStr.FormatFloat(FormatOneDPStr, double(TimeLeft));
21167  //addition at v2.13.0
21168  AnsiString OverallDelayString = AnsiString('\n');
21169  if(int(Train.DelayedRandMins) > 0)
21170  {
21171  if(int(Train.DelayedRandMins) > 1)
21172  {
21173  OverallDelayString = AnsiString("\nTotal random delay ") + AnsiString(int(Train.DelayedRandMins)) + AnsiString(" minutes\n");
21174  }
21175  else
21176  {
21177  OverallDelayString = AnsiString("\nTotal random delay 1 minute\n");
21178  }
21179  }
21180  if(Train.Stopped())
21181  {
21182  TimeToNextMovementStr = "";
21183  }
21184 
21185  if(Train.StoppedAtLocation && (Train.ActionVectorEntryPtr->DepartureTime > TDateTime(-1))) //if departure not next action then ignore NewDelay
21186  { //added at v2.13.0
21187  if(int(Train.NewDelay) == 1)
21188  {
21189  TrainStatusFloat = HeadCode + ": " + Train.Description + ServiceReferenceInfo + '\n' + "Maximum train speed " + MaxSpeedStr +
21190  "km/h (" + MaxMPHStr + "); Power " + PowerStr + "kW" + '\n' + "Mass " + MassStr + "Te; Brakes " + MaxBrakeStr + "Te" + '\n' + SpecialStr + Status + '\n' +
21191  "New random delay here of 1 minute" + OverallDelayString + AnsiString("\nNext: ") + NextStopStr; //changed to Train.Description at v2.16.1
21192  }
21193  else if(int(Train.NewDelay) > 1)
21194  {
21195  TrainStatusFloat = HeadCode + ": " + Train.Description + ServiceReferenceInfo + '\n' + "Maximum train speed " + MaxSpeedStr +
21196  "km/h (" + MaxMPHStr + "); Power " + PowerStr + "kW" + '\n' + "Mass " + MassStr + "Te; Brakes " + MaxBrakeStr + "Te" + '\n' + SpecialStr + Status + '\n' +
21197  "New random delay here of " + AnsiString(int(Train.NewDelay)) + " minutes" + OverallDelayString + AnsiString("\nNext: ") + NextStopStr; //changed to Train.Description at v2.16.1
21198  }
21199  else //int(NewDelay) == 0
21200  {
21201  TrainStatusFloat = HeadCode + ": " + Train.Description + ServiceReferenceInfo + '\n' + "Maximum train speed " + MaxSpeedStr +
21202  "km/h (" + MaxMPHStr + "); Power " + PowerStr + "kW" + '\n' + "Mass " + MassStr + "Te; Brakes " + MaxBrakeStr + "Te" + '\n' + SpecialStr + Status +
21203  OverallDelayString + AnsiString("\nNext: ") + NextStopStr; //changed to Train.Description at v2.16.1
21204  }
21205  }
21206  else if(Train.Stopped()) //stopped anywhere else or not a departure next
21207  {
21208  TrainStatusFloat = HeadCode + ": " + Train.Description + ServiceReferenceInfo + '\n' + "Maximum train speed " + MaxSpeedStr +
21209  "km/h (" + MaxMPHStr + "); Power " + PowerStr + "kW" + '\n' + "Mass " + MassStr + "Te; Brakes " + MaxBrakeStr + "Te" + '\n' + SpecialStr + Status +
21210  OverallDelayString + AnsiString("\nNext: ") + NextStopStr; //changed to Train.Description at v2.16.1
21211  }
21212  else //added mph at v2.15.0
21213  {
21214  TrainStatusFloat = HeadCode + ": " + Train.Description + ServiceReferenceInfo + '\n' + "Maximum train speed " + MaxSpeedStr +
21215  "km/h (" + MaxMPHStr + "); Power " + PowerStr + "kW" + '\n' + "Mass " + MassStr + "Te; Brakes " + MaxBrakeStr + "Te" + '\n' + SpecialStr + Status + ": " +
21216  CurrSpeedStr.FormatFloat(FormatNoDPStr, CurrSpeed) + "km/h (" + CurrSpeedStr.FormatFloat(FormatNoDPStr, (CurrSpeed * 5 / 8)) + "mph)" +
21217  OverallDelayString + AnsiString("\nNext: ") + NextStopStr; //changed to Train.Description at v2.16.1
21218  }
21219  Utilities->CallLogPop(2263);
21220  return(TrainStatusFloat);
21221 }
21222 
21223 // ---------------------------------------------------------------------------
21224 
21225 void TInterface::FlashingGraphics(int Caller, TDateTime Now)
21226 {
21227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FlashingGraphics");
21228 //deal with any failed track elements - flash in zoom-out mode - added at v2.13.0
21229  if(Display->ZoomOutFlag)
21230  {
21231  if(!Track->FailedPointsVector.empty())
21232  {
21233  for(unsigned int x = 0; x < Track->FailedPointsVector.size(); x++)
21234  {
21235  if(WarningFlash)
21236  {
21239  }
21240  else
21241  {
21244  }
21245  }
21246  }
21247  if(!Track->FailedSignalsVector.empty())
21248  {
21249  for(unsigned int x = 0; x < Track->FailedSignalsVector.size(); x++)
21250  {
21251  if(WarningFlash)
21252  {
21255  }
21256  else
21257  {
21260  }
21261  }
21262  }
21263  if(!Track->TSRVector.empty())
21264  {
21265  for(unsigned int x = 0; x < Track->TSRVector.size(); x++)
21266  {
21267  if(WarningFlash)
21268  {
21269  Display->PlotSmallOutput(30, Track->TrackElementAt(1540, Track->TSRVector.at(x).TVPos).HLoc * 4,
21270  Track->TrackElementAt(1546, Track->TSRVector.at(x).TVPos).VLoc * 4, RailGraphics->smBlack);
21271  }
21272  else
21273  {
21274  Display->PlotSmallOutput(31, Track->TrackElementAt(1541, Track->TSRVector.at(x).TVPos).HLoc * 4,
21275  Track->TrackElementAt(1547, Track->TSRVector.at(x).TVPos).VLoc * 4, RailGraphics->smSolidBgnd);
21276  }
21277  }
21278  }
21279  }
21280 
21281 // following section checks to see if GapFlashFlag set & flashes the Gap graphics if so
21282 // Gap flashing is cancelled on any mousedown event
21283 // deal with flashing GapFlash graphics (only in basic mode so no need to check for trains)
21285  {
21286  if(WarningFlash)
21287  {
21288  Track->GapFlashGreen->PlotOverlay(4, Display); // only plotted if PlotOverlay reset
21290  }
21291  else
21292  {
21293  Track->GapFlashGreen->PlotOriginal(17, Display); // only plotted if PlotOverlay set
21295  }
21296  }
21298  {
21299  if(WarningFlash)
21300  {
21305  Display->Update();
21306  }
21307  else
21308  {
21313  Display->Update();
21314  }
21315  }
21316 // deal with gap setting - added at v2.6.1 to make location easier
21318  {
21320  }
21322  {
21323  Display->Ellipse(2, Track->GetGapHLoc() * 16, Track->GetGapVLoc() * 16, clB0G0R5);
21324  }
21326  {
21328  }
21330  {
21331  Display->Ellipse(3, Track->GetGapHLoc() * 16, Track->GetGapVLoc() * 16, clB5G5R5);
21332  }
21333 // deal with other flashing graphics
21335  {
21336  if((Now - RouteFlashStartTime) < TDateTime(RouteFlashDuration / 86400))
21337  {
21338  // cancel if train is moving & arrives on any part of flashing route
21340  {
21341  Track->RouteFlashFlag = false;
21343  ClearandRebuildRailway(18); // because using ConstructRoute->RouteFlash.PlotOriginal() can plot wrong point fillet as well as
21344  // original (if proposed route would change point). With this can dispense with ConstructRoute->RouteFlash.PlotOriginal()
21345  Utilities->CallLogPop(75);
21346  return;
21347  }
21348  InfoPanel->Visible = true;
21349  if(Level2OperMode == PreStart)
21350  {
21351  InfoPanel->Caption = "PRE-START: Route setting in progress";
21352  }
21353  else
21354  {
21355  InfoPanel->Caption = "OPERATING: Route setting in progress";
21356  }
21357  if(WarningFlash)
21358  {
21360  }
21361  else
21362  {
21364  }
21365  }
21366  else
21367  {
21368 // ConstructRoute->RouteFlash.PlotOriginal(); don't need with clearand....
21369 // stop clock while converting route as can take several seconds
21370  TrainController->StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
21372  if(PreferredRouteFlag)
21373  {
21375  }
21376  else
21377  {
21379  }
21380  ConstructRoute->ClearRoute(); // clear it immediately after use so as not to clutter the errorlog
21381  //below addition same as for truncating/cancelling a route and calling on
21382  THVShortPair ExitPair; //added at v2.19.0 to update actions due panel immediately after new route set
21383  if((ActionsDueForm->Visible) && (Level2OperMode == Operating)) // ditto
21384  {
21385  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
21386  {
21387  float LastTimeToExit = TrainController->TrainVectorAt(97, x).TimeToExit;
21388  TrainController->TrainVectorAt(98, x).UpdateTrain(3); //included so 'StoppedAtSignal' flag dropped when route set ahead otherwise
21389  //the 'NOW' entries have to await the next OpTimeToActUpdateCounter update
21390  TrainController->TrainVectorAt(99, x).OpTimeToAct = TrainController->TrainVectorAt(102, x).CalcTimeToAct(4, LastTimeToExit, ExitPair);
21391  }
21394  TrainController->OpTimeToActUpdateCounter = 1; //reset so it won't update for 5 seconds (not 0 so doesn't trigger a further update immediately)
21395  }
21396 
21397  TrainController->BaseTime = TDateTime::CurrentDateTime();
21399  Track->RouteFlashFlag = false;
21401  ClearandRebuildRailway(19); // if drop this ensure replot trains after replot routes else route will overwrite a train
21402  }
21403  }
21404  if(Track->RouteFlashFlag && Display->ZoomOutFlag) // must have entered RouteFlash from normal screen so button states stored
21405  // dropped ZoomOutButton when route or point flashing, but leave this section in in case need to reinstate
21406  // no need to call Clearand... as that is called when revert to normal mode
21407  {
21408  if((Now - RouteFlashStartTime) >= TDateTime(RouteFlashDuration / 86400))
21409  {
21410  Track->RouteFlashFlag = false;
21411  if(PreferredRouteFlag)
21412  {
21414  }
21415  else
21416  {
21418  }
21419  ConstructRoute->ClearRoute(); // clear it immediately after use so as not to clutter the errorlog
21420  }
21421  }
21423  {
21424  if((Now - PointFlashStartTime) < TDateTime((PointsFlashDuration) / 86400))
21425  {
21426  // cancel if train is present on or enters a flashing point, either selected or diverging
21428  {
21430  Track->PointFlashFlag = false;
21432  Utilities->CallLogPop(76);
21433  return;
21434  }
21436  {
21438  Track->PointFlashFlag = false;
21440  Utilities->CallLogPop(77);
21441  return;
21442  }
21443  if(WarningFlash)
21444  {
21447  }
21448  else
21449  {
21451  }
21452  }
21453  else
21454  {
21459  {
21463  }
21465  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
21466  Track->PointFlashFlag = false;
21468  }
21469  }
21471  // dropped ZoomOutButton when point flashing but leave this section in in case need to reinstate
21472  {
21473  if((Now - PointFlashStartTime) < TDateTime((PointsFlashDuration) / 86400))
21474  {
21478  {
21481  }
21482  Track->PointFlashFlag = false;
21484  }
21485  }
21486 // deal with changing level crossings
21487  if(!Track->ChangingLCVector.empty() && (Level2OperMode != Paused))
21488  {
21489  int H;
21490  int V;
21491 
21492  for(unsigned int x = 0; x < Track->ChangingLCVector.size(); x++)
21493  {
21494  bool Manual = false;
21495  if(Track->ChangingLCVector.at(x).TypeOfRoute == 2) // manual
21496  {
21497  Manual = true;
21498  }
21499  H = Track->ChangingLCVector.at(x).HLoc;
21500  V = Track->ChangingLCVector.at(x).VLoc;
21501  if((Now - Track->ChangingLCVector.at(x).StartTime) < TDateTime((Track->ChangingLCVector.at(x).ChangeDuration) / 86400))
21502  // still flashing
21503  {
21504  if(WarningFlash)
21505  {
21506  if(Track->ChangingLCVector.at(x).BarrierState == TTrack::Raising) // closing to trains
21507  {
21508  Track->PlotRaisedLinkedLevelCrossingBarriers(1, Track->ChangingLCVector.at(x).BaseElementSpeedTag, H, V, Display);
21509 // always plots red when raising
21510  }
21511  else
21512  {
21513  Track->PlotLoweredLinkedLevelCrossingBarriers(0, Track->ChangingLCVector.at(x).BaseElementSpeedTag, H, V,
21514  Track->ChangingLCVector.at(x).TypeOfRoute, Display, Manual);
21515  }
21516  }
21517  else
21518  {
21519  Track->PlotLCBaseElementsOnly(2, Track->ChangingLCVector.at(x).BarrierState, Track->ChangingLCVector.at(x).BaseElementSpeedTag, H, V,
21520  Track->ChangingLCVector.at(x).TypeOfRoute, Display);
21521  }
21522  }
21523  else
21524  // flashing period finished
21525  {
21526  if(Track->ChangingLCVector.at(x).BarrierState == TTrack::Raising)
21527  {
21528  Track->PlotRaisedLinkedLevelCrossingBarriers(2, Track->ChangingLCVector.at(x).BaseElementSpeedTag, H, V, Display);
21529 // always plot red when fully raised
21530  Track->SetLinkedLevelCrossingBarrierAttributes(4, H, V, 0); // only set attr to 0 when fully raised
21531  // attributes set to 2 when changing state, now reset to 0, no other actions needed
21532  }
21533  else
21534  // barriers lowering
21535  {
21536  Track->PlotLoweredLinkedLevelCrossingBarriers(1, Track->ChangingLCVector.at(x).BaseElementSpeedTag, H, V,
21537  Track->ChangingLCVector.at(x).TypeOfRoute, Display, Manual);
21538  Track->SetLinkedLevelCrossingBarrierAttributes(5, H, V, 1); // only set attr to 1 when fully lowered
21539  bool FoundFlag;
21540  int TVPos = Track->GetVectorPositionFromTrackMap(46, H, V, FoundFlag);
21541  if(!FoundFlag)
21542  {
21543  throw Exception("Failed to find a route at LC position HLoc = " + (AnsiString)H + " VLoc = " + (AnsiString)V);
21544  }
21545  int RouteNumber;
21546  AllRoutes->GetRouteTypeAndNumber(24, TVPos, 0, RouteNumber); // use 0 for LinkPos, could be 1 or 0 as only a single track element
21547  // don't need returned value of RouteType
21548  if(RouteNumber > -1) // if train crashed then there won't be a routenumber
21549  {
21550  AllRoutes->GetFixedRouteAt(196, RouteNumber).SetRouteSignals(8);
21551  }
21552  }
21553  }
21554  }
21555  for(int x = Track->ChangingLCVector.size() - 1; x >= 0; x--)
21556  {
21557  // now transfer lowering barrier object from the ChangingLCVector to the BarriersDownVector if lowering, reset the start timer (to time the barrier down period)
21558  // and for either raising or lowering erase the object from the ChangingLCVector
21559  if(!Track->IsLCBarrierFlashingAtHV(0, Track->ChangingLCVector.at(x).HLoc, Track->ChangingLCVector.at(x).VLoc))
21560  {
21561  if(Track->ChangingLCVector.at(x).BarrierState == TTrack::Lowering)
21562  {
21563  Track->ChangingLCVector.at(x).StartTime = TrainController->TTClockTime;
21564  Track->ChangingLCVector.at(x).BarrierState = TTrack::Down;
21565  Track->BarriersDownVector.push_back(Track->ChangingLCVector.at(x));
21566  }
21567  Track->ChangingLCVector.erase(Track->ChangingLCVector.begin() + x);
21568  }
21569  }
21570  }
21571  Utilities->CallLogPop(747);
21572 }
21573 
21574 // ---------------------------------------------------------------------------
21575 
21577 // set boundary, Home, NewHome, ZoomOut, CallingOn buttons & menu items as appropriate
21578 {
21579  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetSaveMenuAndButtons");
21580 
21581 // set save railway buttons
21582  bool SaveRailwayButtonsFlag = true;
21583 
21584  SaveRailwayTBPButton->Visible = true;
21585  SaveRailwayPDPButton->Visible = true;
21586  SaveSessionButton->Visible = true;
21587  if(Level1Mode == OperMode)
21588  {
21590  {
21591  SaveRailwayButtonsFlag = false;
21592  }
21593  // set PresetAutoSigRoutesButton enabled or not
21594  // enable if PreStart & no routes set
21595  if((Level2OperMode == PreStart) && (AllRoutes->AllRoutesVector.empty()) && !EveryPrefDir->PrefDirVector.empty())//last condition added at v2.10.0
21596  {
21597  PresetAutoSigRoutesButton->Enabled = true;
21598  }
21599  else
21600  {
21601  PresetAutoSigRoutesButton->Enabled = false;
21602  }
21603  }
21604  else
21605  {
21607  && Track->UserGraphicVector.empty()))
21608  {
21609  SaveRailwayButtonsFlag = false;
21610  }
21611  else if(SavedFileName != "")
21612  {
21613  if((SavedFileName[SavedFileName.Length()] == 'y') || (SavedFileName[SavedFileName.Length()] == 'Y')) // 'rly' file
21614  {
21615  if(!(Track->IsReadyForOperation(false)))
21616  {
21617  SaveRailwayButtonsFlag = false; // can't save under its old name as not now a .rly file
21618  }
21619  }
21620  }
21621  }
21622  if(SaveRailwayButtonsFlag && (Level1Mode == BaseMode))
21623  {
21624  SaveRailwayBaseModeButton->Visible = true;
21625  }
21626  else
21627  {
21628  SaveRailwayBaseModeButton->Visible = false;
21629  }
21630  SaveRailwayTBPButton->Enabled = SaveRailwayButtonsFlag;
21631  SaveRailwayPDPButton->Enabled = SaveRailwayButtonsFlag;
21632  SaveRailwayBaseModeButton->Enabled = SaveRailwayButtonsFlag;
21633  SaveSessionButton->Enabled = SaveRailwayButtonsFlag;
21634 
21635 // set formatted timetable menu item
21636  if(TimetableTitle == "")
21637  {
21638  ExportTTMenuItem->Enabled = false;
21639  }
21640  else
21641  {
21642  ExportTTMenuItem->Enabled = true;
21643  }
21644 // set info menu items
21646  {
21647  FloatingInfoMenu->Enabled = false;
21648  TrackInfoOnOffMenuItem->Enabled = false;
21649  TrainStatusInfoOnOffMenuItem->Enabled = false;
21650  TrainTTInfoOnOffMenuItem->Enabled = false;
21651  }
21652  else
21653  {
21654  FloatingInfoMenu->Enabled = true;
21655  TrackInfoOnOffMenuItem->Enabled = true;
21656  if(Level1Mode == OperMode)
21657  {
21658  TrainStatusInfoOnOffMenuItem->Enabled = true;
21659  TrainTTInfoOnOffMenuItem->Enabled = true;
21660  }
21661  else
21662  {
21663  TrainStatusInfoOnOffMenuItem->Enabled = false;
21664  TrainTTInfoOnOffMenuItem->Enabled = false;
21665  }
21666  }
21667 // set all bar CallingOnButton operational to begin with - no, causes flickering of button graphics,
21668 // work on internal flags & then set buttons according to final flag values, then graphic won't change unless
21669 // there has been a legitimate change of state since the last access
21670 
21671  bool ZoomFlag = true, HomeFlag = true, NewHomeFlag = true, ScreenLeftFlag = true, ScreenRightFlag = true, ScreenUpFlag = true, ScreenDownFlag = true,
21672  TrackBuildPanelEnabledFlag = true, PrefDirPanelEnabledFlag = true, OperatingPanelEnabledFlag = true, TimetablePanelEnabledFlag = true;
21673 
21674  AnsiString TrackBuildPanelLabelCaptionStr = "Build/modify";
21675  AnsiString PrefDirPanelLabelCaptionStr = "Preferred direction selection";
21676  AnsiString OperatingPanelLabelCaptionStr = "Operation";
21677  AnsiString TimetablePanelLabelCaptionStr = "Timetable editor";
21678 
21679  if(!Display->ZoomOutFlag)
21680  {
21681  // prevent if half a screen or less visible (width = 60, height = 36) [Note HLocMin & Max 1 greater than extreme element]
21683  {
21684  ScreenLeftFlag = false; // 60 - 30
21685  }
21687  {
21688  ScreenRightFlag = false; // 60 - (60 - 30)
21689  }
21691  {
21692  ScreenUpFlag = false; // 36 - 18
21693  }
21695  {
21696  ScreenDownFlag = false; // 36 - (36 - 18)
21697  }
21698  }
21699  else
21700  {
21701  // prevent if less than a quarter of a screen visible (width = 240, height = 144)
21703  {
21704  ScreenLeftFlag = false; // 240 - 60
21705  }
21707  {
21708  ScreenRightFlag = false; // 240 - (240 - 60)
21709  }
21711  {
21712  ScreenUpFlag = false; // 144 - 36
21713  }
21715  {
21716  ScreenDownFlag = false; // 144 - (144 - 36)
21717  }
21718  }
21720  {
21721  ZoomFlag = false;
21722  HomeFlag = false;
21723  NewHomeFlag = false;
21724  ScreenLeftFlag = false;
21725  ScreenRightFlag = false;
21726  ScreenUpFlag = false;
21727  ScreenDownFlag = false;
21728  }
21729  if(Display->ZoomOutFlag)
21730  {
21731 // NewHomeFlag = false;
21732  TrackBuildPanelEnabledFlag = false;
21733  TrackBuildPanelLabelCaptionStr = "Disabled";
21734  PrefDirPanelEnabledFlag = false;
21735  PrefDirPanelLabelCaptionStr = "Disabled";
21736  OperatingPanelEnabledFlag = false;
21737  OperatingPanelLabelCaptionStr = "Disabled";
21738  TimetablePanelEnabledFlag = false;
21739  TimetablePanelLabelCaptionStr = "Disabled";
21740  }
21741  if(Level1Mode == OperMode)
21742  {
21743  if(Track->RouteFlashFlag || Track->PointFlashFlag || TTClockAdjPanel->Visible == true || TTClockAdjustWarningPanel->Visible == true ||
21744  MultiplayerHostPanel->Visible == true || MultiplayerPlayerPanel->Visible == true || SkipTTActionsListBox->Visible)
21745  // TTClockAdjPanel added for v2.4.2 to keep it disabled after Clock2Stopped dropped
21746  // host & player panels added for multiplayer
21747  {
21748  MTBFEditBox->Enabled = false;
21749  OperatingPanelEnabledFlag = false;
21750  OperatingPanelLabelCaptionStr = "Disabled";
21751  ZoomFlag = false;
21752  HomeFlag = false;
21753  NewHomeFlag = false;
21754  ScreenLeftFlag = false;
21755  ScreenRightFlag = false;
21756  ScreenUpFlag = false;
21757  ScreenDownFlag = false;
21758  SaveOperatingImageMenuItem->Enabled = false;
21759  }
21760  else
21761  {
21762  MTBFEditBox->Enabled = true;
21763  SaveOperatingImageMenuItem->Enabled = true;
21764  }
21765  }
21766  if(LocationNameTextBox->Visible)
21767  {
21768  ZoomFlag = false;
21769  HomeFlag = false;
21770  NewHomeFlag = false;
21771  ScreenLeftFlag = false;
21772  ScreenRightFlag = false;
21773  ScreenUpFlag = false;
21774  ScreenDownFlag = false;
21775  }
21776  if(TextBox->Visible) // added at v1.3.0 to prevent screen moving when movement keys pressed during text entry
21777  {
21778  ZoomFlag = false;
21779  HomeFlag = false;
21780  NewHomeFlag = false;
21781  ScreenLeftFlag = false;
21782  ScreenRightFlag = false;
21783  ScreenUpFlag = false;
21784  ScreenDownFlag = false;
21785  }
21786  if((Level1Mode == TimetableMode) && (TimetableEditPanel->Visible))
21787  // added at v1.3.0 to prevent screen moving when movement keys pressed during timetable compilation (allowed if TT hidden)
21788  {
21789  ZoomFlag = false;
21790  HomeFlag = false;
21791  NewHomeFlag = false;
21792  ScreenLeftFlag = false;
21793  ScreenRightFlag = false;
21794  ScreenUpFlag = false;
21795  ScreenDownFlag = false;
21796  }
21799  {
21800  ZoomFlag = false;
21801  }
21802  if(ZoomFlag)
21803  {
21804  ZoomButton->Enabled = true;
21805  }
21806  else
21807  {
21808  ZoomButton->Enabled = false;
21809  }
21810  if(HomeFlag)
21811  {
21812  HomeButton->Enabled = true;
21813  }
21814  else
21815  {
21816  HomeButton->Enabled = false;
21817  }
21818  if(NewHomeFlag)
21819  {
21820  NewHomeButton->Enabled = true;
21821  }
21822  else
21823  {
21824  NewHomeButton->Enabled = false;
21825  }
21826  if(ScreenLeftFlag)
21827  {
21828  ScreenLeftButton->Enabled = true;
21829  }
21830  else
21831  {
21832  ScreenLeftButton->Enabled = false;
21833  }
21834  if(ScreenRightFlag)
21835  {
21836  ScreenRightButton->Enabled = true;
21837  }
21838  else
21839  {
21840  ScreenRightButton->Enabled = false;
21841  }
21842  if(ScreenUpFlag)
21843  {
21844  ScreenUpButton->Enabled = true;
21845  }
21846  else
21847  {
21848  ScreenUpButton->Enabled = false;
21849  }
21850  if(ScreenDownFlag)
21851  {
21852  ScreenDownButton->Enabled = true;
21853  }
21854  else
21855  {
21856  ScreenDownButton->Enabled = false;
21857  }
21858  if(OperatingPanelEnabledFlag)
21859  {
21860  OperatingPanel->Enabled = true;
21861  }
21862  else
21863  {
21864  OperatingPanel->Enabled = false;
21865  }
21866  if(TrackBuildPanelEnabledFlag)
21867  {
21868  TrackBuildPanel->Enabled = true;
21869  }
21870  else
21871  {
21872  TrackBuildPanel->Enabled = false;
21873  }
21874  if(PrefDirPanelEnabledFlag)
21875  {
21876  PrefDirPanel->Enabled = true;
21877  }
21878  else
21879  {
21880  PrefDirPanel->Enabled = false;
21881  }
21882  if(TimetablePanelEnabledFlag)
21883  {
21884  TimetablePanel->Enabled = true;
21885  }
21886  else
21887  {
21888  TimetablePanel->Enabled = false;
21889  }
21890  TrackBuildPanelLabel->Caption = TrackBuildPanelLabelCaptionStr;
21891  PrefDirPanelLabel->Caption = PrefDirPanelLabelCaptionStr;
21892  OperatingPanelLabel->Caption = OperatingPanelLabelCaptionStr;
21893  TimetablePanelLabel->Caption = TimetablePanelLabelCaptionStr;
21894 
21895 // check if any CallingOnFlags set & set button accordingly
21896  if(Display->ZoomOutFlag)
21897  {
21898  CallingOnButton->Enabled = false;
21899  CallingOnButton->Down = false;
21900  }
21901  else
21902  {
21903  if(Level2OperMode == Operating)
21904  {
21905  bool CallOnValid = false;
21906  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
21907  {
21909  {
21910  CallingOnButton->Enabled = true;
21911  CallOnValid = true;
21912  }
21913  }
21914  if(!CallOnValid)
21915  {
21916  CallingOnButton->Enabled = false;
21917  CallingOnButton->Down = false;
21918  }
21919  }
21920  else
21921  {
21922  CallingOnButton->Enabled = false;
21923  CallingOnButton->Down = false;
21924  }
21925  }
21926  Utilities->CallLogPop(970);
21927 }
21928 
21929 // ---------------------------------------------------------------------------
21930 
21931 void TInterface::ErrorLog(int Caller, AnsiString Message)
21932 {
21933 // create an error file for diagnostic purposes called on detection of a runtime error
21934 
21935 // Note: For faults in ClockTimer2, after the catch block in ClockTimer2 which calls this function, execution continues from where
21936 // ClockTimer2 was called, so the Utilities->Clock2Stopped flag is cleared and the whole sequence repeats itself, including the fault, until
21937 // the user presses the Exit button. Note also that Utilities->CallLogPop, called when ClockTimer (not ClockTimer2) returns, pops the error
21938 // message off the back of the Utilities->CallLog, not the ClockTimer call. Hence entries keep stacking up, including the push_front entry
21939 // but not the push_back error entry, and when finally printed there is a whole series of entries for the one fault, the number
21940 // depending on the time taken to press Exit.
21941 // Hence introduce an ErrorLogCalledFlag, set to true on first call, and preventing further calls thereafter.
21942 
21943  if(ErrorLogCalledFlag)
21944  {
21945  return;
21946  }
21947  ErrorLogCalledFlag = true;
21948  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + Message);
21949  Utilities->CallLog.push_front("Version: " + ProgramVersion + "; Time and date: " + Utilities->DateTimeStamp());
21950  SaveErrorFile();
21951  if((TempTTFileName != "") && FileExists(TempTTFileName))
21952  {
21953  DeleteFile(TempTTFileName);
21954  }
21955  Display->GetImage()->Visible = false;
21956  PerfLogForm->Visible = false;
21957  ActionsDueForm->Visible = false; // new v2.2.0
21958  TrackBuildPanel->Visible = false;
21959  TrackElementPanel->Visible = false;
21960  LocationNameTextBox->Visible = false;
21961  TextBox->Visible = false;
21962  TrackLengthPanel->Visible = false;
21963  InfoPanel->Visible = false;
21964  PrefDirPanel->Visible = false;
21965  TimetablePanel->Visible = false;
21966  TimetableEditPanel->Visible = false;
21967  TrainController->TTEditPanelVisible = false; // added at v2.6.0 for two location message
21968  OperatingPanel->Visible = false;
21969  FloatingPanel->Visible = false;
21970  ModeMenu->Enabled = false;
21971  SigImagePanel->Visible = false; // new at v2.3.0
21972  FileMenu->Enabled = false;
21973  EditMenu->Enabled = false;
21974  FloatingInfoMenu->Enabled = false;
21975  HelpMenu->Enabled = false;
21976 // SaveHeaderMenu1->Enabled = false;
21977  ScreenLeftButton->Visible = false;
21978  ScreenRightButton->Visible = false;
21979  ScreenUpButton->Visible = false;
21980  ScreenDownButton->Visible = false;
21981  HomeButton->Visible = false;
21982  NewHomeButton->Visible = false;
21983  ZoomButton->Visible = false;
21984  PrefDirKey->Visible = false;
21985  DistanceKey->Visible = false;
21986  LengthHeatmapBitBtn->Visible = false;
21987  SpeedHeatmapBitBtn->Visible = false;
21988  LengthHeatMapImage->Visible = false;
21989  SpeedHeatMapImage->Visible = false;
21990  RecoverClipboardMessageSent = true; // to stop paste message being given if recover clipboard error
21991  OutputLog1->Caption = "";
21992  OutputLog2->Caption = "";
21993  OutputLog3->Caption = "";
21994  OutputLog4->Caption = "";
21995  OutputLog5->Caption = "";
21996  OutputLog6->Caption = "";
21997  OutputLog7->Caption = "";
21998  OutputLog8->Caption = "";
21999  OutputLog9->Caption = "";
22000  OutputLog10->Caption = "";
22001  ErrorMessage->Visible = true;
22002  ErrorButton->Visible = true;
22003  Screen->Cursor = TCursor(-2); // Arrow; - in case was an hourglass
22004 // No need for Utilities->CallLogPop as the call log deque has already been written to file & the next action
22005 // is to close the program when the exit button is pressed
22006 }
22007 
22008 // ---------------------------------------------------------------------------
22009 
22011 // reinstated at v2.13.0 with new form for PerfLogForm //not used from v2.2.0 as now allow floating panel & label to overlie performance panel
22012 {
22013  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsPerfLogFormObscuringFloatingLabel");
22014  if(FloatingPanel->Visible == false)
22015  {
22016  Utilities->CallLogPop(1205);
22017  return(false);
22018  }
22019 // ok if pftop >= flbot, pfbot <= fltop, pfleft >= flright, pfright <= flleft
22020  if((PerfLogForm->Top >= (FloatingPanel->ClientOrigin.y + FloatingPanel->Height)) || ((PerfLogForm->Top + PerfLogForm->Height) <= (FloatingPanel->ClientOrigin.y)) ||
22021  (PerfLogForm->Left >= (FloatingPanel->ClientOrigin.x + FloatingPanel->Width)) || ((PerfLogForm->Left + PerfLogForm->Width) <= (FloatingPanel->ClientOrigin.x)))
22022  {
22023  Utilities->CallLogPop(1206);
22024  return(false);
22025  }
22026  else
22027  {
22028  Utilities->CallLogPop(1207);
22029  return(true);
22030  }
22031 }
22032 
22033 // ---------------------------------------------------------------------------
22034 
22036 // new at v2.13.0
22037 {
22038  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsActionsDueFormObscuringFloatingLabel");
22039  if(FloatingPanel->Visible == false)
22040  {
22041  Utilities->CallLogPop(2539);
22042  return(false);
22043  }
22044 // ok if adftop >= flbot, adfbot <= fltop, adfleft >= flright, adfright <= flleft
22045  if((ActionsDueForm->Top >= (FloatingPanel->ClientOrigin.y + FloatingPanel->Height)) || ((ActionsDueForm->Top + ActionsDueForm->Height) <= (FloatingPanel->ClientOrigin.y)) ||
22046  (ActionsDueForm->Left >= (FloatingPanel->ClientOrigin.x + FloatingPanel->Width)) || ((ActionsDueForm->Left + ActionsDueForm->Width) <= (FloatingPanel->ClientOrigin.x)))
22047  {
22048  Utilities->CallLogPop(2542);
22049  return(false);
22050  }
22051  else
22052  {
22053  Utilities->CallLogPop(2543);
22054  return(true);
22055  }
22056 }
22057 
22058 // ---------------------------------------------------------------------------
22059 
22060 void TInterface::SetCaption(int Caller)
22061 {
22062 /*
22063  NamedRailway; RlyFile; NamedTimetable
22064  n x x "New railway under development";
22065  y n x RailwayTitle + ": under development";
22066  y y n RailwayTitle + ": no timetable loaded";
22067  y y y RailwayTitle + ", " + TimetableTitle;
22068 */
22069 
22070  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetCaption");
22071  if(RailwayTitle == "")
22072  {
22073  Caption = "Railway: New railway under development";
22074  }
22075  else if(!RlyFile)
22076  {
22077  Caption = "Railway: " + RailwayTitle + " under development";
22078  }
22079 // else if(TimetableTitle == "") Caption = "Railway: " + RailwayTitle + "; Timetable: none loaded";
22080  else if(TimetableTitle == "")
22081  {
22082  Caption = "Railway: " + RailwayTitle; // changed at v2.1.0, no need to mention TT if none loaded
22083  }
22084  else
22085  {
22086  Caption = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle;
22087  }
22088  session_api_->dump(); // update session INI file //added at v2.10.0
22089  Utilities->CallLogPop(1208);
22090 }
22091 
22092 // ---------------------------------------------------------------------------
22093 
22094 void TInterface::ResetAll(int Caller)
22095 {
22096  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetAll");
22097  LastNonCtrlOrShiftKeyDown = -1; // added at v2.4.2 to no key down
22099  Track->GapFlashRedPosition = -1;
22100  Track->GapFlashFlag = false;
22101  Track->RouteFlashFlag = false;
22102  Track->PointFlashFlag = false;
22104  AutoSigsFlag = false;
22105  PreventGapOffsetResetting = false;
22106 
22107  Utilities->Clock2Stopped = false;
22108  TTClockSpeed = 1;
22109  TTClockSpeedLabel->Caption = "x1";
22110  Track->SetTrackFinished(false);
22112  CurrentSpeedButton = 0; // not assigned yet
22114  StartX = 0;
22115  StartY = 0;
22116  mbLeftDown = false;
22118  TextOrUserGraphicGridButton->Glyph->LoadFromResourceName(0, "PixelPrecision1");
22120  SigAspectButton->Glyph->LoadFromResourceName(0, "FourAspect");
22122  WarningFlashCount = 0;
22123 
22124  Level1Mode = BaseMode;
22125  SetLevel1Mode(26);
22126  RouteMode = None;
22127  PreferredRoute = true; // default starting conditions
22128  ConsecSignalsRoute = true; // default starting conditions
22129  DevelopmentPanel->Visible = false;
22130 
22131  MainScreen->Canvas->CopyMode = cmSrcCopy;
22132  FloatingPanel->Visible = false;
22133  OverallDistance = 0;
22134  OverallSpeedLimit = -1;
22135  AllRoutes->RouteTruncateFlag = false;
22136  CallingOnButton->Down = false;
22137  Display->ZoomOutFlag = false;
22138  ScreenGridFlag = false;
22139  InfoCaptionStore = "";
22140  ErrorLogCalledFlag = false;
22141  ErrorMessage->Visible = false;
22142  ErrorMessageStoreImage->Visible = false;
22143  TempCursorSet = false;
22144  TempCursor = TCursor(-2); // Arrow
22145  WholeRailwayMoving = false; // new at v2.1.0
22146 
22147  TrainController->TTClockTime = TDateTime(0); // default setting
22148  TTClockAdjPanel->Visible = false;
22150  ConflictPanel->Visible = false;
22151  SelectedTrainID = -1;
22152  SetTrackBuildImages(11);
22153 // TrackInfoOnOffMenuItem->Caption = "Show"; dropped these here at v1.2.0 so don't reset when load a session file
22154 // TrainStatusInfoOnOffMenuItem->Caption = "Show Status";
22155 // TrainTTInfoOnOffMenuItem->Caption = "Show Timetable";
22156  Track->CalcHLocMinEtc(8);
22157  FileChangedFlag = false;
22158  RlyFile = false;
22159  SaveSessionFlag = false;
22160  LoadSessionFlag = false;
22161  SelectionValid = false;
22162  TimetableChangedFlag = false;
22163  SavedFileName = "";
22164  RailwayTitle = "";
22165  TimetableTitle = "";
22166  SetCaption(1);
22167  CreateEditTTFileName = ""; // set to null to allow a check during error file saving, if not null save the tt being edited to the file
22168  // added for Beta v0.2b
22169  CreateEditTTTitle = ""; // as above
22170  AllRoutes->NextRouteID = 0;
22171  TTrain::NextTrainID = 0; // reset to 0 whenever enter operating mode
22172  TFont *TempFont = new TFont; // if try to alter MainScreen->Canvas->Font directly it won't change the style for some reason
22173 
22174  TempFont->Style.Clear();
22175  TempFont->Name = "MS Sans Serif"; // reset font, else stays set to last displayed text font
22176  TempFont->Size = 10;
22177  TempFont->Color = clB0G0R0;
22178  TempFont->Charset = (TFontCharset)(0);
22179  MainScreen->Canvas->Font->Assign(TempFont);
22180  ; // new v2.2.0
22181  // ScreenRightButton->Left = MainScreen->Width + MainScreen->Left; //Button values changed at v2.1.0 to allow for screen resizing
22182  // ScreenLeftButton->Left = ScreenRightButton->Left;
22183  // ScreenUpButton->Left = ScreenRightButton->Left;
22184  // ScreenDownButton->Left = ScreenRightButton->Left;
22185  // HomeButton->Left = ScreenRightButton->Left;
22186  // NewHomeButton->Left = ScreenRightButton->Left;
22187  // ZoomButton->Left = ScreenRightButton->Left;
22188  DevelopmentPanel->Width = MainScreen->Width; //added at v2.18.0
22189  DevelopmentPanel->Top = MainScreen->Top + MainScreen->Height - DevelopmentPanel->Height; // new v2.2.0
22190  DevelopmentPanel->Left = MainScreen->Left; // new v2.2.0 amended at v2.18.0
22191 
22192  delete TempFont;
22193  CtrlKey = false;
22194  ShiftKey = false;
22195  ClipboardChecked = false;
22196  session_api_->reset_all(); // API v1.2
22197  session_api_->dump(); // update session INI file //added at v2.10.0
22198  Utilities->CallLogPop(1209);
22199 }
22200 
22201 // ---------------------------------------------------------------------------
22202 
22204 {
22205  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrackBuildImages,");
22206  if((Level1Mode == OperMode) || RlyFile)
22207  {
22208  TrackLinkedImage->Visible = false;
22209  TrackNotLinkedImage->Visible = false;
22210  GapsSetImage->Visible = false;
22211  GapsNotSetImage->Visible = false;
22212  LocationNamesSetImage->Visible = false;
22213  LocationNamesNotSetImage->Visible = false;
22214  Utilities->CallLogPop(1114);
22215  return;
22216  }
22217  else
22218  {
22219  if(!Track->NoActiveTrack(9))
22220  {
22221  if(Track->IsTrackFinished())
22222  {
22223  TrackLinkedImage->Visible = true;
22224  TrackNotLinkedImage->Visible = false;
22225  }
22226  else
22227  {
22228  TrackNotLinkedImage->Visible = true;
22229  TrackLinkedImage->Visible = false;
22230  }
22231  }
22232  else
22233  {
22234  TrackLinkedImage->Visible = false;
22235  TrackNotLinkedImage->Visible = false;
22236  }
22237  if(!Track->NoGaps(1))
22238  {
22239  if(Track->GapsUnset(6))
22240  {
22241  GapsNotSetImage->Visible = true;
22242  GapsSetImage->Visible = false;
22243  }
22244  else
22245  {
22246  GapsNotSetImage->Visible = false;
22247  GapsSetImage->Visible = true;
22248  }
22249  }
22250  else
22251  {
22252  GapsNotSetImage->Visible = false;
22253  GapsSetImage->Visible = false;
22254  }
22256  {
22257  if(Track->LocationsNotNamed(0))
22258  {
22259  LocationNamesSetImage->Visible = false;
22260  LocationNamesNotSetImage->Visible = true;
22261  }
22262  else
22263  {
22264  LocationNamesSetImage->Visible = true;
22265  LocationNamesNotSetImage->Visible = false;
22266  }
22267  }
22268  else
22269  {
22270  LocationNamesSetImage->Visible = false;
22271  LocationNamesNotSetImage->Visible = false;
22272  }
22273  }
22274  Utilities->CallLogPop(1113);
22275 }
22276 
22277 // ---------------------------------------------------------------------------
22278 
22279 void TInterface::ResetChangedFileDataAndCaption(int Caller, bool NonPrefDirChangesMade)
22280 {
22281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetChangedFileDataAndCaption");
22282  FileChangedFlag = true;
22283  if(NonPrefDirChangesMade)
22284  {
22285  if(RlyFile) // i.e. was a Railway file but major changes made so class as a new railway
22286  {
22287  RailwayTitle = "";
22288  TimetableTitle = "";
22289  SavedFileName = "";
22290  RlyFile = false;
22291  }
22292  TimetableTitle = ""; // should have been reset already during user mode change but include here also
22293  SetTrackBuildImages(15);
22294  }
22295  session_api_->dump(); // update session INI file //added at v2.10.0
22296  SetCaption(2);
22297  Utilities->CallLogPop(1210);
22298 }
22299 
22300 // ---------------------------------------------------------------------------
22301 
22302 void TInterface::SaveSession(int Caller)
22303 {
22304  // ExcessLCDownMins saved as string after ***Interface*** see below
22305  try
22306  {
22307  TrainController->LogEvent("SaveSession");
22308  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSession");
22309  AnsiString CurrentDateTimeStr = "", TimetableTimeStr = "", SessionFileStr = "";
22310  Screen->Cursor = TCursor(-11); // Hourglass;
22311  CurrentDateTimeStr = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
22312  // avoid characters in filename:= / \ : * ? " < > |
22313  TimetableTimeStr = Utilities->Format96HHMMSS(TrainController->TTClockTime);
22314  TimetableTimeStr = TimetableTimeStr.SubString(1, 2) + '.' + TimetableTimeStr.SubString(4, 2) + '.' + TimetableTimeStr.SubString(7, 2);
22315  SessionFileStr = LoadSessionDialog->InitialDir + "\\Session " + CurrentDateTimeStr + "; Timetable time " + TimetableTimeStr + "; " + RailwayTitle +
22316  "; " + TimetableTitle + ".ssn";
22317  std::ofstream SessionFile(SessionFileStr.c_str());
22318  if(SessionFile.fail()) //added at v2.16.0 to give another chance to save
22319  {
22320  TrainController->StopTTClockMessage(150, "Session file failed to open - perhaps the save location\n"
22321  "has been deleted or its name changed?\n\n"
22322  "Will attempt to save in the folder where\n"
22323  "'railway.exe' resides (can move to a more\n"
22324  "appropriate folder manually later).\n");
22325  SessionFile.clear(); //clear flags
22326  SessionFileStr = CurDir + "\\Session " + CurrentDateTimeStr + "; Timetable time " + TimetableTimeStr + "; " + RailwayTitle +
22327  "; " + TimetableTitle + ".ssn";
22328  LoadSessionDialog->InitialDir = CurDir;
22329  SessionFile.open(SessionFileStr.c_str());
22330  }
22331  if(!(SessionFile.fail()))
22332  {
22333  Utilities->SaveFileString(SessionFile, ProgramVersion + ": ***Interface***" + FloatToStr(TrainController->ExcessLCDownMins));
22334 // added ExcessLC... at v2.2.0 as omitted earlier
22335  SaveInterface(0, SessionFile);
22336  // save track elements
22337  Utilities->SaveFileString(SessionFile, "***Track***");
22338  if(Track->UserGraphicVector.empty())
22339  {
22340  Track->SaveTrack(4, SessionFile, false); // false for no graphics (**Active elements** saved as marker)
22341  }
22342  else
22343  {
22344  Track->SaveTrack(11, SessionFile, true); // true for graphics to be saved (**Active elements**1 saved as marker)
22345  }
22346  // save text elements
22347  Utilities->SaveFileString(SessionFile, "***Text***");
22348  TextHandler->SaveText(2, SessionFile);
22349  // save PrefDir elements
22350  Utilities->SaveFileString(SessionFile, "***PrefDirs***");
22351  EveryPrefDir->SavePrefDirVector(2, SessionFile);
22352  if(!Track->UserGraphicVector.empty())
22353  {
22354  // save user graphics
22355  Track->SaveUserGraphics(2, SessionFile);
22356  }
22357  // save routes
22358  Utilities->SaveFileString(SessionFile, "***Routes***");
22359  AllRoutes->SaveRoutes(0, SessionFile);
22360  // save LockedRoutes
22361  Utilities->SaveFileString(SessionFile, "***Locked routes***");
22362  TrainController->SaveSessionLockedRoutes(0, SessionFile);
22363  // save ContinuationAutoSigEntries
22364  Utilities->SaveFileString(SessionFile, "***ContinuationAutoSigEntries***");
22366  // save BarriersDownVector
22367  Utilities->SaveFileString(SessionFile, "***BarriersDownVector***");
22368  Track->SaveSessionBarriersDownVector(0, SessionFile);
22369  // save timetable
22370  Utilities->SaveFileString(SessionFile, "***Timetable***");
22371  if(!(SaveTimetableToSessionFile(0, SessionFile, SessionFileStr))) //includes the timetable itself + TrainOperatingData
22372  {
22373  SessionFile.close();
22374  DeleteFile(SessionFileStr);
22375  Screen->Cursor = TCursor(-2); // Arrow;
22376  TrainController->StopTTClockMessage(3, "Error saving file, unable to save session");
22377  Utilities->CallLogPop(1150);
22378  return;
22379  }
22380  // save TimetableClock
22381  Utilities->SaveFileString(SessionFile, "***TimetableClock***");
22382  Utilities->SaveFileDouble(SessionFile, double(TrainController->TTClockTime));
22383 
22384  // save trains
22385  Utilities->SaveFileString(SessionFile, "***Trains***");
22386  TrainController->SaveSessionTrains(0, SessionFile);
22387  // save performance file
22388  Utilities->SaveFileString(SessionFile, "***Performance file***");
22389  SavePerformanceFile(0, SessionFile);
22390  Utilities->SaveFileString(SessionFile, "***End of performance file***");
22391 
22392 /* The following additions are for later program versions where new features need to be saved in sessions.
22393 In each case need to ensure that the following points are considered and dealt with:
22394 
22395  1) New program works ok with old session files
22396  2) Old programs work ok with extended session file
22397  3) Check what happens to changed railway/track/trains/timetable etc when an old prog loads a new session file
22398  4) Ensure that the last character saved for a record change can't be 'E' as that used to identify the end of the additions
22399 */
22400  Utilities->SaveFileString(SessionFile, "***Additions after v2.3.1***");
22401 
22402 // additions at v2.4.0 to save TrainController->AvHoursIntValue
22405  Utilities->SaveFileString(SessionFile, "***Failed Trains***");
22406  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
22407  {
22409  {
22412  }
22413  }
22414  Utilities->SaveFileInt(SessionFile, -1); // marker for end of failed trains
22415  Utilities->SaveFileString(SessionFile, "End of file at v2.4.0");
22416 // end of v2.4.0 additions
22417 
22418 // addition at v2.7.0
22419  Utilities->SaveFileBool(SessionFile, ConsecSignalsRoute);
22420  Utilities->SaveFileString(SessionFile, "End of file at v2.7.0");
22421 // end of v2.7.0 addition
22422 
22423 // additions at v2.9.1
22427  Utilities->SaveFileDouble(SessionFile, double(TrainController->TotEarlyExitMins));
22428  Utilities->SaveFileDouble(SessionFile, double(TrainController->TotLateExitMins));
22429  Utilities->SaveFileString(SessionFile, "End of file at v2.9.1"); //changed from '2.9.0' at v2.9.2
22430 // end of v2.9.1 additions
22431 
22432 //additions at v2.11.0 - SkippedTTEvents
22434  // add data for trains in process of skipping timetable events (i.e. those with events after a future departure)
22435  if(!TrainController->TrainVector.empty())
22436  {
22437  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
22438  {
22439  TTrain Train = TrainController->TrainVectorAt(82, x);
22440  if(Train.SkippedDeparture)
22441  {
22442  Utilities->SaveFileInt(SessionFile, Train.TrainID);
22443  Utilities->SaveFileBool(SessionFile, Train.SkippedDeparture);
22444  Utilities->SaveFileBool(SessionFile, Train.ActionsSkippedFlag);
22445  Utilities->SaveFileInt(SessionFile, Train.SkipPtrValue);
22446  Utilities->SaveFileInt(SessionFile, Train.TrainSkippedEvents);
22447  }
22448  }
22449  }
22450  Utilities->SaveFileString(SessionFile, "End of file at v2.11.0");
22451 //end of v2.11.0 additions
22452 
22453 //additions at v2.12.0 - become new service early & treat pass as departure
22454  if(!TrainController->TrainVector.empty())
22455  {
22456  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
22457  {
22458  TTrain Train = TrainController->TrainVectorAt(85, x);
22459  if(Train.TreatPassAsTimeLocDeparture) //this can only apply once for a single train (service repeats are separate trains)
22460  {
22461  Utilities->SaveFileInt(SessionFile, Train.TrainID);
22462  }
22463  }
22464  }
22465  Utilities->SaveFileString(SessionFile, "End of file at v2.12.0");
22466 //end of v2.12.0 additions
22467 
22468 //additions at v2.13.0 - random delays & failures
22469 //No need to save Utilities->LastDelayTTClockTime - makes little difference and would cause corruption in any v2.13.0 Beta sessions
22470  Utilities->SaveFileInt(SessionFile, Utilities->CumulativeDelayedRandMinsAllTrains); //to allow for exited and removed trains
22471  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
22472  { //if empty will skip
22473  TTrain Train = TrainController->TrainVectorAt(87, x);
22474  Utilities->SaveFileDouble(SessionFile, Train.NewDelay);
22475  Utilities->SaveFileDouble(SessionFile, Train.DelayedRandMins);
22477  Utilities->SaveFileDouble(SessionFile, double(Train.ActualArrivalTime));
22478  //ReleaseTime already loaded
22479  }
22480  //save failed point info
22481  Utilities->SaveFileInt(SessionFile, Track->FailedPointsVector.size()); //number of failed points
22482  for(unsigned int x = 0; x < Track->FailedPointsVector.size(); x++)
22483  { //if empty will skip, when reload set Failed to true & SpeedLimits to 10km/h
22484  TTrackElement &TE = Track->TrackElementAt(1512, Track->FailedPointsVector.at(x).TVPos);
22485  Utilities->SaveFileInt(SessionFile, Track->FailedPointsVector.at(x).TVPos);
22488  Utilities->SaveFileDouble(SessionFile, double(Track->FailedPointsVector.at(x).FailureTime));
22489  Utilities->SaveFileDouble(SessionFile, double(Track->FailedPointsVector.at(x).RepairTime));
22490  }
22491  //save failed signal info
22492  Utilities->SaveFileInt(SessionFile, Track->FailedSignalsVector.size()); //number of failed signals
22493  for(unsigned int x = 0; x < Track->FailedSignalsVector.size(); x++)
22494  { //if empty will skip, when reload set Failed to true
22495  Utilities->SaveFileInt(SessionFile, Track->FailedSignalsVector.at(x).TVPos);
22496  Utilities->SaveFileDouble(SessionFile, double(Track->FailedSignalsVector.at(x).FailureTime));
22497  Utilities->SaveFileDouble(SessionFile, double(Track->FailedSignalsVector.at(x).RepairTime));
22498  }
22499  //save TSR info
22500  Utilities->SaveFileInt(SessionFile, Track->TSRVector.size()); //number of TSRs
22501  for(unsigned int x = 0; x < Track->TSRVector.size(); x++)
22502  { //if empty will skip, when reload set Failed to true & SpeedLimit to 10km/h
22503  TTrackElement &TE = Track->TrackElementAt(1532, Track->TSRVector.at(x).TVPos);
22504  Utilities->SaveFileInt(SessionFile, Track->TSRVector.at(x).TVPos);
22506  Utilities->SaveFileDouble(SessionFile, double(Track->TSRVector.at(x).FailureTime));
22507  Utilities->SaveFileDouble(SessionFile, double(Track->TSRVector.at(x).RepairTime));
22508  }
22509  Utilities->SaveFileString(SessionFile, "End of file at v2.13.0");
22510 //end of v2.13.0 additions
22511 
22512 //additions at v2.14.0 - delays and failures separated & now saved with session
22513  if(Utilities->DelayMode == Minor)
22514  {
22515  Utilities->SaveFileInt(SessionFile, 1);
22516  }
22517  else if(Utilities->DelayMode == Moderate)
22518  {
22519  Utilities->SaveFileInt(SessionFile, 2);
22520  }
22521  else if(Utilities->DelayMode == Major)
22522  {
22523  Utilities->SaveFileInt(SessionFile, 3);
22524  }
22525  else
22526  {
22527  Utilities->SaveFileInt(SessionFile, 0);
22528  }
22529  if(Utilities->FailureMode == FMinor)
22530  {
22531  Utilities->SaveFileInt(SessionFile, 1);
22532  }
22533  else if(Utilities->FailureMode == FModerate)
22534  {
22535  Utilities->SaveFileInt(SessionFile, 2);
22536  }
22537  else if(Utilities->FailureMode == FMajor)
22538  {
22539  Utilities->SaveFileInt(SessionFile, 3);
22540  }
22541  else
22542  {
22543  Utilities->SaveFileInt(SessionFile, 0);
22544  }
22545  Utilities->SaveFileString(SessionFile, "End of file at v2.14.0");
22546 //end of v2.14.0 additions
22547 
22548 //additions at v2.16.1 - train descriptions
22549  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
22550  {
22551  Utilities->SaveFileString(SessionFile, TVIt->Description);
22552  }
22553  Utilities->SaveFileString(SessionFile, "End of file at v2.16.1");
22554 //end of v2.16.1 additions
22555 
22556 //additions at v2.19.0 - reminders save AnsiString of trainvector position + '-' + action vector position of exch reminder
22557  AnsiString ReminderEntry = "";
22558  if(!TrainController->TrainVector.empty())
22559  {
22560  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
22561  {
22562  TTrain Train = TrainController->TrainVectorAt(103, x);
22564  for(unsigned int y = 0; y < AV.size(); y++)
22565  {
22566  if(AV.at(y).Reminder > 0)
22567  {
22568  ReminderEntry = AnsiString(x + (10000 * AV.at(y).Reminder)) + '-' + AnsiString(y); //allows up to 9999 trains
22569  Utilities->SaveFileString(SessionFile, ReminderEntry);
22570  }
22571  }
22572  }
22573  }
22574  Utilities->SaveFileString(SessionFile, "End of file at v2.19.0");
22575 //end of v2.19.0 additions
22576 
22577 //additions at v2.20.2 - AllowedToPassRedSignal flag for each train - without this could save a session after permission given but when reloaded would give a SPAD
22578  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
22579  {
22580  Utilities->SaveFileBool(SessionFile, TVIt->AllowedToPassRedSignal); //correct here but loaded incorrectly for 1st train. see LoadSession for v2.20.2
22581  }
22582  Utilities->SaveFileString(SessionFile, "End of file at v2.20.2");
22583 //end of v2.20.2 additions
22584 
22585 //additions at v2.20.3 - remaining train values
22586 
22587  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
22588  {
22589  TTrain TempTrain = *TVIt; //for diagnostics as debugger can't handle iterators
22590  Utilities->SaveFileBool(SessionFile, TVIt->RemainHereLogNotSent);
22591  Utilities->SaveFileString(SessionFile, TVIt->HeadCode);
22592  Utilities->SaveFileBool(SessionFile, TVIt->FinishJoinLogSent);
22593  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoFrontSplitMessage);
22594  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoRearSplitMessage);
22595  Utilities->SaveFileBool(SessionFile, TVIt->FailedTrainNoFinishJoinMessage);
22596  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoJoinedByMessage);
22597  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoCDTMessage);
22598  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoNewServiceMessage);
22599  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoNewShuttleFromNonRepeatMessage);
22600  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoRepeatShuttleMessage);
22601  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerNoRepeatShuttleOrNewServiceMessage);
22602  Utilities->SaveFileBool(SessionFile, TVIt->ZeroPowerDepartMessage);
22603  Utilities->SaveFileBool(SessionFile, TVIt->TrainInFrontMessage);
22604  Utilities->SaveFileBool(SessionFile, TVIt->TrainFailurePending);
22605  Utilities->SaveFileBool(SessionFile, TVIt->BufferZoomOutFlashRequired);
22606  Utilities->SaveFileBool(SessionFile, TVIt->JoinedOtherTrainFlag);
22607  Utilities->SaveFileBool(SessionFile, TVIt->LastSigPassedFailed);
22608  Utilities->SaveFileBool(SessionFile, TVIt->LeavingUnderSigControlAtContinuation);
22609  Utilities->SaveFileBool(SessionFile, TVIt->SignallerStoppingFlag);
22610  Utilities->SaveFileBool(SessionFile, TVIt->StationStopCalculated);
22611  Utilities->SaveFileBool(SessionFile, TVIt->StepForwardFlag);
22612  Utilities->SaveFileDouble(SessionFile, TVIt->SignallerStopBrakeRate);
22613  Utilities->SaveFileDouble(SessionFile, TVIt->MinsDelayed);
22614  Utilities->SaveFileDouble(SessionFile, TVIt->OpTimeToAct);
22615  Utilities->SaveFileDouble(SessionFile, TVIt->TimeToExit);
22616  Utilities->SaveFileDouble(SessionFile, TVIt->FirstLaterStopRecoverableTime);
22617  Utilities->SaveFileInt(SessionFile, TVIt->DistanceToStationStop);
22618  Utilities->SaveFileInt(SessionFile, TVIt->UpdateCounter);
22619  Utilities->SaveFileInt(SessionFile, TVIt->ExitPair.first);
22620  Utilities->SaveFileInt(SessionFile, TVIt->ExitPair.second);
22621  Utilities->SaveFileBool(SessionFile, TVIt->StoppedWithoutPower);
22622  Utilities->SaveFileBool(SessionFile, TVIt->TrainInFront);
22623  Utilities->SaveFileInt(SessionFile, TVIt->OldZoomOutElement[0]);
22624  Utilities->SaveFileInt(SessionFile, TVIt->OldZoomOutElement[1]);
22625  Utilities->SaveFileInt(SessionFile, TVIt->OldZoomOutElement[2]);
22626  Utilities->SaveFileString(SessionFile, TVIt->SelReminderString);
22627  Utilities->SaveFileString(SessionFile, TVIt->SelSkipString);
22628  Utilities->SaveFileBool(SessionFile, TVIt->AllowedToPassRedSignal); //corrected earlier error in v2.20.2 LoadSession, see above
22629  Utilities->SaveFileString(SessionFile, "****"); //end of train marker
22630  }
22631  Utilities->SaveFileString(SessionFile, "End of file at v2.20.3");
22632 //end of v2.20.3 additions
22633 
22634 //additions at v2.23.0
22635  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
22636  {
22637  Utilities->SaveFileBool(SessionFile, TVIt->NonDefaultMinDwellTimeFlag);
22638  Utilities->SaveFileDouble(SessionFile, TVIt->ArrivalMinDwellTime); //may be the default value of 30secs
22639 // Utilities->SaveFileDouble(SessionFile, double(TVIt->ActualArrivalTime)); //already loaded in v2.13.0 additions
22640  }
22641  Utilities->SaveFileString(SessionFile, "End of file at v2.23.0");
22642 //end of v2.23.0 additions
22643 
22644 //IF ADD MORE PARAMETERS REMEMBER TO ADD TO ERROR FILE TOO, BUT CHANGE 'SessionFile' to 'ErrorFile'
22645 
22646  SessionFile.close();
22647  TrainController->StopTTClockMessage(4, "Session saved: Session " + CurrentDateTimeStr + "; Timetable time " + TimetableTimeStr + "; " +
22648  RailwayTitle + "; " + TimetableTitle + ".ssn");
22650 // to restore the ability to reselect Ctrl S after a save (FormKeyUp doesn't work because the Interface form doesn't have focus)
22651  }
22652  else
22653  {
22654  TrainController->StopTTClockMessage(5, "Session file failed to open - reason not known, unable to save."); //after 2nd attempt to save
22655  }
22657  Screen->Cursor = TCursor(-2); // Arrow
22658  Utilities->CallLogPop(1141);
22659  }
22660  catch(const Exception &e) //non-error catch at v2.14.0
22661  {
22662  TrainController->StopTTClockMessage(138, "Session file failed to save\nError message: " + e.Message);
22663  Screen->Cursor = TCursor(-2); // Arrow;
22664  Utilities->CallLogPop(2554);
22665  }
22666 }
22667 
22668 // ---------------------------------------------------------------------------
22669 
22670 void TInterface::LoadSession(int Caller)
22671 // always loads in 'Paused' or 'PreStart' mode
22672 {
22673 // remember to load the timetable clock
22674 // no routes in build
22675 // prob need to set 'OperMode' then 'Paused', ensure have all info needed for these
22676 // set buttons enabled to correspond to flags on reloading. If no PrefDirs then disable all route buttons
22677 // set RlyFile true
22678  try
22679  {
22680  TrainController->LogEvent("LoadSession");
22681  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSession");
22682  if(!ClearEverything(4))
22683  {
22684  Utilities->CallLogPop(1145);
22685  return;
22686  }
22687  LoadSessionDialog->Filter = "Session file (*.ssn)|*.ssn";
22688  if(LoadSessionDialog->Execute())
22689  {
22690  if(LoadSessionDialog->InitialDir != TPath::GetDirectoryName(LoadSessionDialog->FileName)) // new at v2.6.0 to retain a new directory
22691  {
22692  LoadSessionDialog->InitialDir = TPath::GetDirectoryName(LoadSessionDialog->FileName);
22693  }
22694  TrainController->LogEvent("LoadSession " + LoadSessionDialog->FileName);
22695  Screen->Cursor = TCursor(-11); // Hourglass;
22696  if(SessionFileIntegrityCheck(0, AnsiString(LoadSessionDialog->FileName).c_str()))
22697  // if(true)
22698  {
22699  std::ifstream SessionFile(AnsiString(LoadSessionDialog->FileName).c_str());
22700  if(!(SessionFile.fail()))
22701  {
22702  if(Utilities->PerformanceFile.is_open()) //added at v2.17.0 because may still be open if an earlier session load failed
22703  {
22704  Utilities->PerformanceFile.close();
22705  }
22706  TrainController->AvHoursIntValue = 0; // initial value set at v2.4.0 in case not changed later
22707  TrainController->MTBFHours = 0; // initial value set at v2.4.0 in case not changed later
22708  AnsiString TempString = Utilities->LoadFileString(SessionFile);
22709 // "version + : ***Interface***" + at v2.2.0 ExcessLCDownMins (omitted earlier)
22710 
22711  int LastCharBeforeFloat = TempString.LastDelimiter('*'); // added at v2.2.0
22712  if((LastCharBeforeFloat == 0) || (LastCharBeforeFloat == TempString.Length()))
22713  // can't find it or no value for Excess LCDownMins, either way count as zero
22714  {
22716  }
22717  else
22718  {
22719  AnsiString FloatStr = TempString.SubString(LastCharBeforeFloat + 1, TempString.Length() - LastCharBeforeFloat);
22720  if(!Utilities->CheckStringDouble(FloatStr)) // returns false for empty string or invalid double
22721  {
22723  }
22724  else
22725  {
22726  TrainController->ExcessLCDownMins = FloatStr.ToDouble();
22727  }
22728  } // end of v2.2.0 * v2.4.0 additions
22729 
22730  LoadInterface(0, SessionFile);
22731  int TempDisplayOffsetH = Display->DisplayOffsetH; // stored as they are zeroed when track loaded
22732  int TempDisplayOffsetV = Display->DisplayOffsetV;
22733  int TempDisplayOffsetHHome = Display->DisplayOffsetHHome;
22734  int TempDisplayOffsetVHome = Display->DisplayOffsetVHome;
22735  bool GraphicsFollow = false;
22736  // load track elements
22737  TempString = Utilities->LoadFileString(SessionFile); // ***Track***"
22738  Track->LoadTrack(4, SessionFile, GraphicsFollow);
22739  if(!Track->IsTrackFinished()) //added at v2.13.0, should have been included earlier
22740  {
22741  SessionFile.close();
22742  Screen->Cursor = TCursor(-2); // Arrow;
22743  ShowMessage("Track not linked, session can't be loaded");
22744  Utilities->CallLogPop(2532);
22745  return;
22746  }
22747  // load text elements
22748  TempString = Utilities->LoadFileString(SessionFile); // ***Text***"
22749  TextHandler->LoadText(1, SessionFile);
22750  // load PrefDir elements
22751  TempString = Utilities->LoadFileString(SessionFile); // "***PrefDirs***"
22752  EveryPrefDir->LoadPrefDir(1, SessionFile);
22753  if(GraphicsFollow)
22754  {
22755  Track->LoadGraphics(1, SessionFile, CurDir + "\\" + USERGRAPHICS_DIR_NAME); // include path to Graphics folder);
22756  }
22758  {
22759  SessionFile.close();
22760  Screen->Cursor = TCursor(-2); // Arrow;
22761  ShowMessage("Corruption in preferred direction section of the session file, session can't be loaded");
22762  Utilities->CallLogPop(1438);
22763  return;
22764  }
22765  // load routes
22766  TempString = Utilities->LoadFileString(SessionFile); // "***Routes***"
22767  if(!AllRoutes->LoadRoutes(0, SessionFile))
22768  {
22769  SessionFile.close();
22770  Screen->Cursor = TCursor(-2); // Arrow;
22771  ShowMessage("Corruption in route section of the session file, session can't be loaded");
22772  Utilities->CallLogPop(1439);
22773  return;
22774  }
22775  // load LockedRoutes
22776  TempString = Utilities->LoadFileString(SessionFile); // "***Locked routes***"
22777  TrainController->LoadSessionLockedRoutes(0, SessionFile);
22778  // load ContinuationAutoSigEntries
22779  TempString = Utilities->LoadFileString(SessionFile); // "***ContinuationAutoSigEntries***"
22781  // load BarriersDownVector if present, but ensure backwards compatibility with earlier files
22782  TempString = Utilities->LoadFileString(SessionFile); // "***BarriersDownVector***" or "***Timetable***"
22783  if(TempString == "***BarriersDownVector***")
22784  {
22785  Track->LoadBarriersDownVector(0, SessionFile);
22786  TempString = Utilities->LoadFileString(SessionFile); // "***Timetable***"
22787  }
22788  // load timetable (marker "***Timetable***" already loaded)
22789  if(!(LoadTimetableFromSessionFile(0, SessionFile)))
22790  {
22791  SessionFile.close();
22792  Screen->Cursor = TCursor(-2); // Arrow;
22793  ShowMessage("Unable to load timetable section of the session file, session can't be loaded");
22794  Utilities->CallLogPop(1151);
22795  return;
22796  }
22797  // TimetableTitle should be loaded at this stage - check
22798  if(TimetableTitle == "")
22799  {
22800  SessionFile.close();
22801  Screen->Cursor = TCursor(-2); // Arrow;
22802  throw Exception("TimetableTitle null in LoadSession");
22803  }
22804 
22805  session_api_->write_string("session_file", LoadSessionDialog->FileName); // API v1.2
22806  session_api_->find_metadata_file(); // API v1.2
22807 
22808  // load timetable clock
22809  TempString = Utilities->LoadFileString(SessionFile); // ***TimetableClock***
22810  TrainController->RestartTime = TDateTime(Utilities->LoadFileDouble(SessionFile)); // ClockTime set in RestartSessionOperMode;
22811  // load trains
22812  TempString = Utilities->LoadFileString(SessionFile); // ***Trains***
22813  TrainController->LoadSessionTrains(0, SessionFile);
22814  // load performance file + populate the performance log
22815  TempString = Utilities->LoadFileString(SessionFile); // ***Performance file***
22816  // first reset the performance file name and open it before reloading it
22817  PerformanceFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
22818  // avoid characters in filename:= / \ : * ? " < > |
22819  PerformanceFileName = CurDir + "\\" + PERFLOG_DIR_NAME + "\\Log " + PerformanceFileName + "; " + RailwayTitle + "; " +
22820  TimetableTitle + ".txt";
22821  Utilities->PerformanceFile.open(PerformanceFileName.c_str(), std::ios_base::out);
22822  if(Utilities->PerformanceFile.fail())
22823  {
22824  ShowMessage("Performance logfile failed to open, logs won't be saved. Ensure that there is a folder named " + PERFLOG_DIR_NAME +
22825  " in the folder where the 'Railway.exe' program file resides");
22826  }
22827  // now reload the performance file (also populates PerformanceLogBox)
22828  LoadPerformanceFile(0, SessionFile);
22829 
22830 // addition at v2.4.0 (train failures)
22831  char TempChar;
22832  SessionFile.get(TempChar);
22833  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) // get rid of all end of lines & emerge with eof or '*'
22834  {
22835  SessionFile.get(TempChar);
22836  }
22837  if(SessionFile.eof()) // old session file
22838  {
22839  TrainController->AvHoursIntValue = 0; //initialise these for new prog
22841  SessionFile.close(); // no TrainController->AvHoursIntValue & no failed trains
22842  goto FINISHEDLOADING; //don't like goto's normally but necessary here to avoid multiple else's
22843  }
22844  { //remainder enclosed in a block so goto doesn't bypass initialisation of a local variable (DummyStr, ID etc.)
22845  AnsiString DummyStr = Utilities->LoadFileString(SessionFile); // "**Additions after v2.3.1***" discarded (first '*' loaded earlier)
22846  TrainController->AvHoursIntValue = Utilities->LoadFileInt(SessionFile); // TrainController->AvHoursIntValue added at v2.4.0
22847  TrainController->NumFailures = Utilities->LoadFileInt(SessionFile); // number of train failures
22848  TrainController->MTBFHours = TrainController->AvHoursIntValue; // TTClockSpeed set to 1 in RestartSessionMode so no need to include here
22849  // now load any failed trains along with their OriginalPowerAtRail values
22850  DummyStr = Utilities->LoadFileString(SessionFile); // discard "***Failed Trains***"
22851  int ID = Utilities->LoadFileInt(SessionFile); // train ID or -1 for no more failed trains
22852  double PowerDouble;
22853  while(ID != -1)
22854  {
22855  PowerDouble = Utilities->LoadFileDouble(SessionFile); // ok TrainVector loaded at this stage (loaded in LoadSessionTrains)
22858  ID = Utilities->LoadFileInt(SessionFile);
22859  }
22860 //end of 2.4.0 addition
22861 // addition at v2.7.0 - need value for ConsecSignalsRoute but might have eof here with older sessions so first test for that
22862  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.4.0" discarded
22863 
22864  SessionFile.get(TempChar);
22865  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) // get rid of all end of lines & emerge with eof or '0' or '1'
22866  {
22867  SessionFile.get(TempChar);
22868  }
22869  if(SessionFile.eof()) //old session file
22870  {
22871  SessionFile.close(); //ConsecSignalsRoute set to PrefDirRoute
22872  goto FINISHEDLOADING;
22873  }
22874  if((TempChar != '0') && (TempChar != '1'))
22875  {
22876  throw Exception("TempChar value = " + AnsiString(TempChar) + ", should be 0 or 1");
22877  }
22878  ConsecSignalsRoute = true;
22879  if(TempChar == '0')
22880  {
22881  ConsecSignalsRoute = false;
22882  // at v2.7.0 if find eof then don't need a value for ConsecSignalsRoute as that loaded during LoadInterface
22883  }
22884  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.7.0" discarded
22885 //end of 2.7.0 additions
22886 //additions at v2.9.1 - added exit information for performance log (should have been included earlier)
22887  SessionFile.get(TempChar);
22888  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) // get rid of all end of lines & emerge with 1st digit of EarlyExits as character
22889  {
22890  SessionFile.get(TempChar);
22891  }
22892  if(SessionFile.eof()) //old session file
22893  {
22894  TrainController->EarlyExits = 0; //initialise for new prog - added at v2.13.0, should heve been here before
22899  SessionFile.close();
22900  goto FINISHEDLOADING;
22901  }
22902  //TempChar now contains the first digit of EarlyExits as an ASCII character, so get the rest up to CRLF
22903  TempString = TempChar;
22904  SessionFile.get(TempChar);
22905  while((TempChar != '\n') && (TempChar != '\0'))
22906  {
22907  TempString = TempString + TempChar;
22908  SessionFile.get(TempChar);
22909  }
22910  TrainController->EarlyExits = TempString.ToInt();
22913  TrainController->TotEarlyExitMins = float(Utilities->LoadFileDouble(SessionFile));
22914  TrainController->TotLateExitMins = float(Utilities->LoadFileDouble(SessionFile));
22915  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.9.1" discarded
22916 //end of 2.9.1 additions
22917 
22918 //2.11.0 additions - skip timetabled events
22919  SessionFile.get(TempChar);
22920  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
22921  {// get rid of all end of lines & emerge with eof or 1st digit of TrainController->SkippedTTEvents
22922  SessionFile.get(TempChar);
22923  }
22924  if(SessionFile.eof()) //old session file
22925  {
22926  TrainController->SkippedTTEvents = 0; //initialise for new prog, added at v2.13.0, should have been here earlier
22927  SessionFile.close(); //no need to initialise train data as initialised during creation of a new train
22928  goto FINISHEDLOADING;
22929  }
22930  //TempChar now contains the first digit of SkippedTTEvents as a character, so get the rest up to CRLF
22931  TempString = TempChar;
22932  SessionFile.get(TempChar);
22933  while((TempChar != '\n') && (TempChar != '\0'))
22934  {
22935  TempString = TempString + TempChar;
22936  SessionFile.get(TempChar);
22937  }
22938  //here have SkippedTTEvents as AnsiString in TempString
22939  TrainController->SkippedTTEvents = TempString.ToInt();
22940  //now get the skip data for each train (may or may not be any)
22941  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
22942  {// get rid of all end of lines & emerge with eof or 1st digit of TrainID
22943  SessionFile.get(TempChar);
22944  }
22945  if(SessionFile.eof()) //no train skip data
22946  {
22947  SessionFile.close();
22948  goto FINISHEDLOADING;
22949  }
22950  //TempChar now contains the 1st digit of TrainID as a character or 'E'
22951  TempString = TempChar;
22952  while(TempString != 'E') //'E' is 1st character of 'End of file at v2.11.0'
22953  {
22954  SessionFile.get(TempChar);
22955  while((TempChar != '\n') && (TempChar != '\0'))
22956  {
22957  TempString = TempString + TempChar;
22958  SessionFile.get(TempChar);
22959  }
22960  //here have TrainID as AnsiString in TempString
22961  TTrain &Train = TrainController->TrainVectorAtIdent(57, TempString.ToInt());
22962  Train.SkippedDeparture = Utilities->LoadFileBool(SessionFile);
22963  Train.ActionsSkippedFlag = Utilities->LoadFileBool(SessionFile);
22964  Train.SkipPtrValue = Utilities->LoadFileInt(SessionFile);
22965  Train.TrainSkippedEvents = Utilities->LoadFileInt(SessionFile);
22966  SessionFile.get(TempChar); //will be '\n' as LoadFileInt doesn't extract newlines from the stream
22967  TempString = TempChar; //added at v2.12.0 as a safeguard in case not '\n' in which case it would retain its old value
22968  while((TempChar == '\n') || (TempChar == '\0')) //get rid of any excess non-significant chars
22969  {
22970  SessionFile.get(TempChar); //get the next one
22971  TempString = TempChar; //when emerge this will be 'E' or 1st digit of TrainID
22972  }
22973  }
22974  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.11.0" discarded ('E' already loaded)
22975 //end of 2.11.0 additions
22976 
22977 //additions at v2.12.0 - change to new service early
22978  SessionFile.get(TempChar);
22979  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
22980  {// get rid of all end of lines & emerge with eof or 1st digit of a TrainID or 'E'
22981  SessionFile.get(TempChar);
22982  }
22983  if(SessionFile.eof()) //old session file
22984  {
22985  SessionFile.close(); //no need to initialise train data as initialised during creation of a new train
22986  goto FINISHEDLOADING;
22987  }
22988  //TempChar now contains the first digit of the first train ID which requires TreatPassAsTimeLocDeparture to be set, or 'E' if none
22989  TempString = TempChar;
22990  while(TempString != 'E') //'E' is 1st character of 'End of file at v2.12.0'
22991  {
22992  SessionFile.get(TempChar);
22993  while((TempChar != '\n') && (TempChar != '\0'))
22994  {
22995  TempString = TempString + TempChar;
22996  SessionFile.get(TempChar);
22997  }
22998  //here have TrainID as AnsiString in TempString & '\n' in TempChar
22999  TTrain &Train = TrainController->TrainVectorAtIdent(62, TempString.ToInt());
23000  Train.TreatPassAsTimeLocDeparture = true;
23001  SessionFile.get(TempChar); //will be 'E' or 1st digit of next TrainID as character if more than one, or \n if more than one \n character
23002  TempString = TempChar;
23003  while((TempChar == '\n') || (TempChar == '\0')) //get rid of any excess non-significant chars, unlikely to be any
23004  {
23005  SessionFile.get(TempChar); //get the next one
23006  TempString = TempChar;
23007  }
23008  }
23009  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.12.0" discarded ('E' already loaded)
23010 //end of additions at v2.12.0
23011 
23012 //additions at v2.13.0 - random delays - location delays, failed points & signals (DelayMode not saved here, but saved later with FailureMode)
23013  SessionFile.get(TempChar);
23014  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
23015  {// get rid of all end of lines & emerge with eof or 1st digit of CumulativeDelayedRandMinsAllTrains
23016  SessionFile.get(TempChar);
23017  }
23018  if(SessionFile.eof()) //old session file
23019  {
23020  Utilities->CumulativeDelayedRandMinsAllTrains = 0; //initialised for new prog
23021  SessionFile.close(); //no need to initialise train data or Track.Failed as initialised during LoadSessionTrains and LoadTrack
23022  goto FINISHEDLOADING;
23023  }
23024  //TempChar now contains the first digit of CumulativeDelayedRandMinsAllTrains
23025  TempString = "";
23026  while((TempChar != '\n') && (TempChar != '\0'))
23027  {
23028  TempString = TempString + TempChar;
23029  SessionFile.get(TempChar);
23030  }
23031  //here have CumulativeDelayedRandMinsAllTrains as AnsiString in TempString & '\n' in TempChar
23032  Utilities->CumulativeDelayedRandMinsAllTrains = TempString.ToDouble(); //if invalid will fail with a non-error catch
23033  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++) //trains already loaded, if no trains then load DummyStr
23034  { //if empty will skip
23035  double TempDouble;
23036  TTrain &Train = TrainController->TrainVectorAt(88, x);
23037  Train.NewDelay = Utilities->LoadFileDouble(SessionFile);
23038  Train.DelayedRandMins = Utilities->LoadFileDouble(SessionFile);
23040  TempDouble = Utilities->LoadFileDouble(SessionFile);
23041  Train.ActualArrivalTime = TDateTime(TempDouble); //ReleaseTime already loaded
23042  }
23043  //load failed point info
23044  int TempInt, VecSize;
23045  double TempDouble;
23046  VecSize = Utilities->LoadFileInt(SessionFile); //number of failed points
23048  for(int x = 0; x < VecSize; x++)
23049  {
23050  TempInt = Utilities->LoadFileInt(SessionFile); //TVPos
23051  TTrackElement &TE = Track->TrackElementAt(1513, TempInt);
23052  TE.Failed = true;
23053  FPVE.TVPos = TempInt;
23054  TempInt = Utilities->LoadFileInt(SessionFile); //TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
23056  TempInt = Utilities->LoadFileInt(SessionFile); //TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
23058  TempDouble = Utilities->LoadFileDouble(SessionFile); //FailureTime
23059  FPVE.FailureTime = TempDouble;
23060  TempDouble = Utilities->LoadFileDouble(SessionFile); //RepairTime
23061  FPVE.RepairTime = TempDouble;
23062  Track->FailedPointsVector.push_back(FPVE);
23063  }
23064  //load failed signal info
23065  VecSize = Utilities->LoadFileInt(SessionFile); //number of failed points
23066  for(int x = 0; x < VecSize; x++)
23067  {
23068  TempInt = Utilities->LoadFileInt(SessionFile); //TVPos
23069  TTrackElement &TE = Track->TrackElementAt(1533, TempInt);
23070  TE.Failed = true;
23071  FPVE.TVPos = TempInt;
23072  TempDouble = Utilities->LoadFileDouble(SessionFile); //FailureTime
23073  FPVE.FailureTime = TempDouble;
23074  TempDouble = Utilities->LoadFileDouble(SessionFile); //RepairTime
23075  FPVE.RepairTime = TempDouble;
23076  Track->FailedSignalsVector.push_back(FPVE);
23077  }
23078  //load TSR info
23079  VecSize = Utilities->LoadFileInt(SessionFile); //number of TSRs
23080  for(int x = 0; x < VecSize; x++)
23081  {
23082  TempInt = Utilities->LoadFileInt(SessionFile); //TVPos
23083  TTrackElement &TE = Track->TrackElementAt(1534, TempInt);
23084  TE.Failed = true;
23085  FPVE.TVPos = TempInt;
23086  TempInt = Utilities->LoadFileInt(SessionFile); //TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
23088  TempDouble = Utilities->LoadFileDouble(SessionFile); //FailureTime
23089  FPVE.FailureTime = TempDouble;
23090  TempDouble = Utilities->LoadFileDouble(SessionFile); //RepairTime
23091  FPVE.RepairTime = TempDouble;
23092  Track->TSRVector.push_back(FPVE);
23093  }
23094  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.13.0" discarded
23095 //end of additions at v2.13.0
23096 
23097 //additions at v2.14.0 - delays and failures
23098  SessionFile.get(TempChar);
23099  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
23100  {// get rid of all end of lines & emerge with eof or digit that represents DelayMode (0, 1, 2 or 3)
23101  SessionFile.get(TempChar);
23102  }
23103  if(SessionFile.eof()) //old session file, initialise delays & failures to none
23104  {
23105  NoDelaysMenuItem->Enabled = false;
23106  MinorDelaysMenuItem->Enabled = true;
23107  ModerateDelaysMenuItem->Enabled = true;
23108  MajorDelaysMenuItem->Enabled = true;
23109  DelayMenu->Caption = "No delays";
23110  PerfLogForm->PerformanceLog(51, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": No random delays selected on loading session");
23111  Utilities->DelayMode = Nil;
23112 
23113  NoFailuresMenuItem->Enabled = false;
23114  MinorFailuresMenuItem->Enabled = true;
23115  ModerateFailuresMenuItem->Enabled = true;
23116  MajorFailuresMenuItem->Enabled = true;
23120  FailureMenu->Caption = "No failures";
23121  PerfLogForm->PerformanceLog(52, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": No random failures selected on loading session");
23123  SessionFile.close();
23124  goto FINISHEDLOADING;
23125  }
23126  //TempChar now contains the digit that represents DelayMode (0, 1, 2 or 3)
23127  if(TempChar == '0')
23128  {
23129  NoDelaysMenuItem->Enabled = false;
23130  MinorDelaysMenuItem->Enabled = true;
23131  ModerateDelaysMenuItem->Enabled = true;
23132  MajorDelaysMenuItem->Enabled = true;
23133  DelayMenu->Caption = "No delays";
23134  PerfLogForm->PerformanceLog(53, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": No random delays selected on loading session");
23135  Utilities->DelayMode = Nil;
23136  }
23137  else if(TempChar == '1')
23138  {
23139  NoDelaysMenuItem->Enabled = true;
23140  MinorDelaysMenuItem->Enabled = false;
23141  ModerateDelaysMenuItem->Enabled = true;
23142  MajorDelaysMenuItem->Enabled = true;
23143  DelayMenu->Caption = "Minor delays";
23144  PerfLogForm->PerformanceLog(54, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Minor random delays selected on loading session");
23146  }
23147  else if(TempChar == '2')
23148  {
23149  NoDelaysMenuItem->Enabled = true;
23150  MinorDelaysMenuItem->Enabled = true;
23151  ModerateDelaysMenuItem->Enabled = false;
23152  MajorDelaysMenuItem->Enabled = true;
23153  DelayMenu->Caption = "Moderate delays";
23154  PerfLogForm->PerformanceLog(55, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Moderate random delays selected on loading session");
23156  }
23157  else if(TempChar == '3')
23158  {
23159  NoDelaysMenuItem->Enabled = true;
23160  MinorDelaysMenuItem->Enabled = true;
23161  ModerateDelaysMenuItem->Enabled = true;
23162  MajorDelaysMenuItem->Enabled = false;
23163  DelayMenu->Caption = "Major delays";
23164  PerfLogForm->PerformanceLog(56, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Major random delays selected on loading session");
23166  }
23167  else
23168  {
23169  throw Exception("Session file DelayMode not in range"); //non-error catch later
23170  }
23171  SessionFile.get(TempChar); //should be '\n'
23172  SessionFile.get(TempChar); //FailureMode
23173  if(TempChar == '0')
23174  {
23175  NoFailuresMenuItem->Enabled = false;
23176  MinorFailuresMenuItem->Enabled = true;
23177  ModerateFailuresMenuItem->Enabled = true;
23178  MajorFailuresMenuItem->Enabled = true;
23182  FailureMenu->Caption = "No failures";
23183  PerfLogForm->PerformanceLog(57, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": No random failures selected on loading session");
23185  }
23186  else if(TempChar == '1')
23187  {
23188  NoFailuresMenuItem->Enabled = true;
23189  MinorFailuresMenuItem->Enabled = false;
23190  ModerateFailuresMenuItem->Enabled = true;
23191  MajorFailuresMenuItem->Enabled = true;
23195  FailureMenu->Caption = "Minor failures";
23196  PerfLogForm->PerformanceLog(58, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Minor random failures selected on loading session");
23198  }
23199  else if(TempChar == '2')
23200  {
23201  NoFailuresMenuItem->Enabled = true;
23202  MinorFailuresMenuItem->Enabled = true;
23203  ModerateFailuresMenuItem->Enabled = false;
23204  MajorFailuresMenuItem->Enabled = true;
23208  FailureMenu->Caption = "Moderate failures";
23209  PerfLogForm->PerformanceLog(59, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Moderate random failures selected on loading session");
23211  }
23212  else if(TempChar == '3')
23213  {
23214  NoFailuresMenuItem->Enabled = true;
23215  MinorFailuresMenuItem->Enabled = true;
23216  ModerateFailuresMenuItem->Enabled = true;
23217  MajorFailuresMenuItem->Enabled = false;
23221  FailureMenu->Caption = "Major failures";
23222  PerfLogForm->PerformanceLog(60, Utilities->Format96HHMMSS(TrainController->RestartTime) + ": Major random failures selected on loading session");
23224  }
23225  else
23226  {
23227  throw Exception("Session file FailureMode not in range"); //non-error catch later
23228  }
23229  SessionFile.get(TempChar); //should be '\n'
23230  DummyStr = Utilities->LoadFileString(SessionFile); // "End of file at v2.14.0" discarded
23231 //end of v2.14.0 additions
23232 
23233 //additions at v2.16.1 - train descriptions
23234  SessionFile.get(TempChar);
23235  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0')))
23236  {// get rid of all end of lines & emerge with eof or character that represents first train's description
23237  SessionFile.get(TempChar);
23238  }
23239  if(SessionFile.eof()) //old session file, no train descriptions, no changes of description
23240  {
23241  SessionFile.close(); //no need to initialise anything as initialisation will occur during BuildTrainDataVectorForLoadFile
23242  goto FINISHEDLOADING;
23243  }
23244  //TempChar now contains the first digit of first train's description or 'E' for 'End of file at v2.16.1'
23245  TempString = "";
23246  while((TempChar != '\n') && (TempChar != '\0'))
23247  {
23248  TempString = TempString + TempChar;
23249  SessionFile.get(TempChar);
23250  }
23251  //here have first train's description as an AnsiString in TempString or 'End of file at v2.16.1' & '\0' in TempChar
23252  if(TempString == "End of file at v2.16.1") //added at v2.17.0, if TempString == 'End of file at v2.16.1' then without this
23253  { //load 'End of file at v2.16.1' into first description, and there is nothing after
23254  goto NEXTADDITION; //except '\n' at very end of file (after last NUL) so
23255  } //TVIt->Description = Utilities->LoadFileString(SessionFile); loops indefinitely
23256  //as never finds '\0'
23257  //changed FINISHEDLOADING to NEXTADDITION at v2.20.2 as may be more additions
23258 
23259  if(!TrainController->TrainVector.empty())
23260  {
23261  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23262  {
23263  if(TVIt == TrainController->TrainVector.begin())
23264  {
23265  TVIt->Description = TempString;
23266  }
23267  else
23268  {
23269  TVIt->Description = Utilities->LoadFileString(SessionFile);
23270  }
23271  }
23272  }
23273  DummyStr = Utilities->LoadFileString(SessionFile); //"End of file at v2.16.1" discarded
23274 //end of v2.16.1 additions
23275 
23276 NEXTADDITION:
23277 //additions at v2.19.0 - reminders in form of AnsiString "x-y" where x = train vector position & y is action vector position
23278  SessionFile.get(TempChar);
23279  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) //get rid of all end of lines & emerge with eof or 1st char of 1st reminder
23280  { //or 'E' for 'End of...'
23281  SessionFile.get(TempChar);
23282  }
23283  if(SessionFile.eof()) // old session file
23284  {
23285  //no initialisations needed as all 'Reminder' values initialised to 0 during program initialisation
23286  SessionFile.close();
23287  goto FINISHEDLOADING;
23288  }
23289  AnsiString ReminderEntry = "";
23290  //TempChar now contains the 1st char of 1st reminder or 'E' for 'End of file at v2.19.0'
23291  TempString = "";
23292  while((TempChar != '\n') && (TempChar != '\0'))
23293  {
23294  TempString = TempString + TempChar;
23295  SessionFile.get(TempChar);
23296  }
23297  //here have first train's reminder as an AnsiString in TempString or 'End of file at v2.19.0' & '\0' in TempChar
23298  while(TempString != "End of file at v2.19.0")
23299  {
23300  int PosDash = TempString.Pos('-');
23301  if(PosDash == 0) //can't find it, will create an error as it shouldn't ever be
23302  {
23303  break;
23304  }
23305  else
23306  {
23307  AnsiString x = TempString.SubString(1, PosDash - 1);
23308  //x is Reminder * 10000 + Train vector position as an AnsiString, so separate them
23309  unsigned int TrainVecPos = x.SubString(2, 4).ToInt(); //last 4 characters converted to int
23310  unsigned int Reminder = x.SubString(1, 1).ToInt(); //1st character converted to int, 1, 2, 3 or 4
23311  unsigned int y = TempString.SubString(PosDash + 1, TempString.Length() - PosDash).ToInt(); //= AV vector position
23312  TTrain &Train = TrainController->TrainVectorAt(105, TrainVecPos);
23314  AV.at(y).Reminder = Reminder;
23315  TempString = Utilities->LoadFileString(SessionFile);
23316  }
23317  }
23318 // Don't need to load DummyString = "End of file at v2.19.0" as it's already been loaded into TempString
23319 
23320 //additions at v2.20.2 - AllowedToPassRedSignal flag for each train - without this could save a session after permission given but when reloaded would give a SPAD
23321  SessionFile.get(TempChar);
23322  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) //get rid of all end of lines & emerge with eof or 1st train's bool of AllowedToPassRedSignal
23323  {
23324  SessionFile.get(TempChar);
23325  }
23326  if(SessionFile.eof()) // old session file
23327  {
23328  //no initialisations needed as all 'AllowedToPassRedSignal' values initialised to false during program initialisation
23329  SessionFile.close();
23330  goto FINISHEDLOADING;
23331  }
23332  if(!TrainController->TrainVector.empty())
23333  {//TempChar now contains 1st train's bool of AllowedToPassRedSignal ('End of file at v2.19.0' already loaded above)' <--WRONG - see below!
23334  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23335  {
23336  if(TVIt == TrainController->TrainVector.begin())
23337  {
23338  TVIt->AllowedToPassRedSignal = TempChar; //<---this isn't a boolean, '0' or '1' will load as true for the first train!!!
23339  } //corrected in later addition - see below
23340  else
23341  {
23342  TVIt->AllowedToPassRedSignal = Utilities->LoadFileBool(SessionFile);
23343  }
23344  }
23345  }
23346  DummyStr = Utilities->LoadFileString(SessionFile); //"End of file at v2.20.2" discarded
23347 //end of v2.20.2 additions
23348 
23349 //additions at v2.20.3 to load all remaining train values
23350  SessionFile.get(TempChar);
23351  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) //get rid of all end of lines & emerge with eof or 1st train's bool of RemainHereLogNotSent
23352  {
23353  SessionFile.get(TempChar);
23354  }
23355  if(SessionFile.eof()) // old session file
23356  {
23357  //no initialisations needed as no new variables introduced
23358  SessionFile.close();
23359  goto FINISHEDLOADING;
23360  }
23361  if(!TrainController->TrainVector.empty())
23362  {//TempChar now contains 1st train's char of RemainHereLogNotSent or 'E' from 'End of file at v2.20.3'
23363  if((TempChar == '0') || (TempChar == '1')) //should be as there are trains present
23364  {
23365  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23366  {
23367 // TTrain &TempTrain = *TVIt; //for diagnosics, debugger can't handle iterators, reference so it updates as *TVIt updates
23368  if(TVIt == TrainController->TrainVector.begin())
23369  {
23370  TVIt->RemainHereLogNotSent = true;
23371  if(TempChar == '0')
23372  {
23373  TVIt->RemainHereLogNotSent = false;
23374  }
23375  }
23376  else
23377  {
23378  TVIt->RemainHereLogNotSent = Utilities->LoadFileBool(SessionFile);
23379  }
23380  AnsiString HeadCode = Utilities->LoadFileString(SessionFile); //included for diagnostics
23381  TVIt->FinishJoinLogSent = Utilities->LoadFileBool(SessionFile);
23382  TVIt->ZeroPowerNoFrontSplitMessage = Utilities->LoadFileBool(SessionFile);
23383  TVIt->ZeroPowerNoRearSplitMessage = Utilities->LoadFileBool(SessionFile);
23384  TVIt->FailedTrainNoFinishJoinMessage = Utilities->LoadFileBool(SessionFile);
23385  TVIt->ZeroPowerNoJoinedByMessage = Utilities->LoadFileBool(SessionFile);
23386  TVIt->ZeroPowerNoCDTMessage = Utilities->LoadFileBool(SessionFile);
23387  TVIt->ZeroPowerNoNewServiceMessage = Utilities->LoadFileBool(SessionFile);
23388  TVIt->ZeroPowerNoNewShuttleFromNonRepeatMessage = Utilities->LoadFileBool(SessionFile);
23389  TVIt->ZeroPowerNoRepeatShuttleMessage = Utilities->LoadFileBool(SessionFile);
23390  TVIt->ZeroPowerNoRepeatShuttleOrNewServiceMessage = Utilities->LoadFileBool(SessionFile);
23391  TVIt->ZeroPowerDepartMessage = Utilities->LoadFileBool(SessionFile);
23392  TVIt->TrainInFrontMessage = Utilities->LoadFileBool(SessionFile);
23393  TVIt->TrainFailurePending = Utilities->LoadFileBool(SessionFile);
23394  TVIt->BufferZoomOutFlashRequired = Utilities->LoadFileBool(SessionFile);
23395  TVIt->JoinedOtherTrainFlag = Utilities->LoadFileBool(SessionFile);
23396  TVIt->LastSigPassedFailed = Utilities->LoadFileBool(SessionFile);
23397  TVIt->LeavingUnderSigControlAtContinuation = Utilities->LoadFileBool(SessionFile);
23398  TVIt->SignallerStoppingFlag = Utilities->LoadFileBool(SessionFile);
23399  TVIt->StationStopCalculated = Utilities->LoadFileBool(SessionFile);
23400  TVIt->StepForwardFlag = Utilities->LoadFileBool(SessionFile);
23401  TVIt->SignallerStopBrakeRate = Utilities->LoadFileDouble(SessionFile);
23402  double MD = Utilities->LoadFileDouble(SessionFile);
23403  double OTTA = Utilities->LoadFileDouble(SessionFile);
23404  double TTE = Utilities->LoadFileDouble(SessionFile);
23405  double FLSRT = Utilities->LoadFileDouble(SessionFile);
23406  TVIt->MinsDelayed = (float)MD;
23407  TVIt->OpTimeToAct = (float)OTTA;
23408  TVIt->TimeToExit = (float)TTE;
23409  TVIt->FirstLaterStopRecoverableTime = (float)FLSRT;
23410  TVIt->DistanceToStationStop = Utilities->LoadFileInt(SessionFile);
23411  int UD = Utilities->LoadFileInt(SessionFile);
23412  TVIt->UpdateCounter = (unsigned int)UD;
23413  int EP1 = Utilities->LoadFileInt(SessionFile);
23414  int EP2 = Utilities->LoadFileInt(SessionFile);
23415  TVIt->ExitPair.first = EP1;
23416  TVIt->ExitPair.second = EP2;
23417  TVIt->StoppedWithoutPower = Utilities->LoadFileBool(SessionFile);
23418  TVIt->TrainInFront = Utilities->LoadFileBool(SessionFile);
23419  TVIt->OldZoomOutElement[0] = Utilities->LoadFileInt(SessionFile);
23420  TVIt->OldZoomOutElement[1] = Utilities->LoadFileInt(SessionFile);
23421  TVIt->OldZoomOutElement[2] = Utilities->LoadFileInt(SessionFile);
23422  TVIt->SelReminderString = Utilities->LoadFileString(SessionFile); //this and the next are UnicodeStrings but will convert from AnsiString for non-complex characters
23423  TVIt->SelSkipString = Utilities->LoadFileString(SessionFile);
23424  TVIt->AllowedToPassRedSignal = Utilities->LoadFileBool(SessionFile); //corrected earlier error in v2.20.2, see above
23425  AnsiString Marker = Utilities->LoadFileString(SessionFile); //'****'
23426  }
23427  }
23428  }
23429  DummyStr = Utilities->LoadFileString(SessionFile); //"End of file at v2.20.3" discarded 'E' may have been loaded earlier
23430 //end of v2.20.3 additions
23431 //additions at v2.23.0
23432  SessionFile.get(TempChar); //if not eof TempChar contains 1st train's bool of NonDefaultMinDwellTimeFlag or if there are no trains 'E' of 'End of file at v2.23.0'
23433  while(!SessionFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) //get rid of all end of lines & emerge with eof or 1st train's bool of RemainHereLogNotSent
23434  {
23435  SessionFile.get(TempChar);
23436  }
23437  if(SessionFile.eof()) // old session file
23438  {
23439  if(!TrainController->TrainVector.empty())//initialse the new variables
23440  {
23441  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23442  {
23443  TVIt->NonDefaultMinDwellTimeFlag = false;
23444  TVIt->ArrivalMinDwellTime = 30.0;
23445  }
23446  }
23447  SessionFile.close();
23448  goto FINISHEDLOADING;
23449  }
23450  if(!TrainController->TrainVector.empty())
23451  {//TempChar now contains 1st train's bool of NonDefaultMinDwellTimeFlag
23452  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23453  {
23454  if(TVIt == TrainController->TrainVector.begin())
23455  {
23456  TVIt->NonDefaultMinDwellTimeFlag = (TempChar == '1');
23457  }
23458  else
23459  {
23460  TVIt->NonDefaultMinDwellTimeFlag = Utilities->LoadFileBool(SessionFile);
23461  }
23462  TVIt->ArrivalMinDwellTime = Utilities->LoadFileDouble(SessionFile);
23463  if(!TVIt->NonDefaultMinDwellTimeFlag)
23464  {
23465  TVIt->ArrivalMinDwellTime = 30.0; //just in case hadn't been initialised, to ensure has a value
23466  }
23467 //AnsiString cc = TVIt->HeadCode; //diagnostics
23468 //bool aa = TVIt->NonDefaultMinDwellTimeFlag;
23469 //double bb = TVIt->ArrivalMinDwellTime;
23470  }
23471  }
23472  DummyStr = Utilities->LoadFileString(SessionFile); //"End of file at v2.23.0" discarded, 'E' may have been loaded earlier
23473 //end of v2.23.0 additions
23474  } //this is the final block closure after all additions (enclosed in a block so goto doesn't bypass initialisation of a local variable (DummyStr, ID etc.))
23475 
23476 FINISHEDLOADING:
23477  if(SessionFile.is_open())
23478  {
23479  SessionFile.close();
23480  }
23481  // deal with other settings
23482  //first check if there are any trains without descriptions and if so allocate them from TrainDataEntryPtr->FixedDescription
23483  if(!TrainController->TrainVector.empty()) //added at v2.17.0 as old session files won't have train descriptions
23484  {
23485  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
23486  {
23487  if(TVIt->Description == "")
23488  {
23489  TVIt->Description = TVIt->TrainDataEntryPtr->FixedDescription;
23490  }
23491  }
23492  }
23493  Display->DisplayOffsetH = TempDisplayOffsetH; // reset to saved values
23494  Display->DisplayOffsetV = TempDisplayOffsetV;
23495  Display->DisplayOffsetHHome = TempDisplayOffsetHHome;
23496  Display->DisplayOffsetVHome = TempDisplayOffsetVHome;
23497  // now set attributes to 1 for all LCs with barriers down
23498  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++)
23499  {
23501  }
23502  Track->ChangingLCVector.clear();
23503  Track->CalcHLocMinEtc(10);
23505  SetLevel1Mode(27);
23506  if(Level2OperMode == PreStart)
23507  // this section added at v1.3.2 as otherwise only in MainScreenMouseDown2, and if load a session without clicking mouse on screen
23508  {
23509  // then delay unspecified though seems to be 0
23510  PointsFlashDuration = 0.0;
23513  }
23514  else
23515  {
23519  }
23520  RlyFile = true;
23521  SetCaption(3);
23522 // TrainController->ReplotTrains(2, Display); //to set track elements with train IDs //removed at v2.22.0 as caused EnterLongServRefAsName to be called
23523  //when LongServRefsFlag is set without StaticFeaturesDisplay being populated (only populated in
23524  //Clearand... and that not called yet). Instead just set the track elements with TrainIDs
23525  if(!TrainController->TrainVector.empty()) //this part sets track element with TrainIDs without calling EnterLongServRefAsName
23526  {
23527  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
23528  {
23529  if(!TrainController->TrainVectorAt(106, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
23530  { //see Kevin Smith error information for details
23531  for(int y = 0; y < 4; y++)
23532  {
23534  }
23535  }
23536  }
23537  }
23538 
23539  //now routes and trains loaded and TrainIDs set (in ReplotTrains) set all the signals as a session file saved before v2.14.0 will have incorrect
23540  //aspects if there are facing ground signals on a route new at v2.14.0
23541  if(AllRoutes->AllRoutesVector.size() > 0)
23542  {
23543  for(TAllRoutes::TAllRoutesVectorIterator ARVIt = AllRoutes->AllRoutesVector.begin(); ARVIt < AllRoutes->AllRoutesVector.end(); ARVIt++)
23544  {
23545  ARVIt->SetRouteSignals(13);
23546  }
23547  }
23548  //set TrainInFront if appropriate
23549  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt < TrainController->TrainVector.end(); TVIt++)
23550  {
23551  TTrain &Tr = *TVIt;
23552  if((Tr.LeadElement > -1) && (Tr.LeadExitPos > -1))//this section added at v2.18.0
23553  {
23554  int NextPos = Track->TrackElementAt(1673, Tr.LeadElement).Conn[Tr.LeadExitPos];
23555  if(NextPos > -1)
23556  {
23557  int NextEntryPos = Track->TrackElementAt(650, Tr.LeadElement).ConnLinkPos[Tr.LeadExitPos];
23558  if(Track->OtherTrainOnTrack(17, NextPos, NextEntryPos, Tr.TrainID))
23559  // true if another train on NextEntryPos track whether bridge or not
23560  {
23561  Tr.TrainInFront = true;
23562  }
23563  else
23564  {
23565  Tr.TrainInFront = false;
23566  }
23567  }
23568  }
23569  }
23571  }
23572  }
23573  else
23574  {
23575  ShowMessage("Session file integrity check failed, unable to load " + LoadSessionDialog->FileName +
23576  ".\n\nPlease make sure that the timetable has been validated and that you are using the latest program version.");
23577  }
23578  Screen->Cursor = TCursor(-2); // Arrow;
23579  }
23580  Utilities->CallLogPop(1146);
23581  }
23582  catch(const Exception &e)
23583  {
23584  if((e.Message.Pos("esource") > 0) || (e.Message.Pos("arameter") > 0)) // 'Resource or Parameter, avoid capitals as may be OS dependent
23585  {
23586  Screen->Cursor = TCursor(-2); // Arrow;
23587  OutputLog1->Caption = "";
23588  OutputLog2->Caption = "";
23589  OutputLog3->Caption = "";
23590  OutputLog4->Caption = "";
23591  OutputLog5->Caption = "";
23592  OutputLog6->Caption = "";
23593  OutputLog7->Caption = "";
23594  OutputLog8->Caption = "";
23595  OutputLog9->Caption = "";
23596  OutputLog10->Caption = "";
23597  UnicodeString MessageStr =
23598  "Insufficient memory available to load the file, and partial load likely to be corrupt. Application will terminate. Try loading the session as the first action after reloading the program.";
23599 // UnicodeString MessageStr = "Last train loaded = " + UnicodeString(TrainController->LastTrainLoaded); //for debugging session train loading for many trains
23600  Application->MessageBox(MessageStr.c_str(), L"Out of memory", MB_OK | MB_ICONERROR);
23601  Application->Terminate();
23602  }
23603  else //non-error catch at v2.14.0
23604  {
23605  TrainController->StopTTClockMessage(96, "Session file failed to load - is the latest program version in use?\nIf so then the file may be corrupt.\n\nError message: " + e.Message);
23606  Screen->Cursor = TCursor(-2); // Arrow;
23607  Utilities->CallLogPop(2441);
23608  }
23609  }
23610 }
23611 
23612 // ---------------------------------------------------------------------------
23613 
23614 void TInterface::SaveInterface(int Caller, std::ofstream &SessionFile)
23615 {
23616  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveInterface");
23617  if(Level2OperMode == PreStart)
23618  {
23619  Utilities->SaveFileString(SessionFile, AnsiString("PreStart"));
23620  }
23621  else
23622  {
23623  Utilities->SaveFileString(SessionFile, AnsiString("NotPreStart")); // covers both Paused & Operating
23624  }
23625  Utilities->SaveFileString(SessionFile, RailwayTitle);
23626  Utilities->SaveFileString(SessionFile, TimetableTitle);
23627  Utilities->SaveFileBool(SessionFile, PreferredRoute);
23628  Utilities->SaveFileBool(SessionFile, PreferredRoute);
23629 // changed at v2.7.0, was ConsecSignalsRoute here but if different to PreferredRoute (as can be from v2.7.0) then have
23630  Utilities->SaveFileBool(SessionFile, AutoSigsFlag);
23631 // error if try to load with an earlier prog version. ConsecSignalsRoute now moved to end of session file
23632  Utilities->SaveFileInt(SessionFile, Display->DisplayOffsetH);
23633  Utilities->SaveFileInt(SessionFile, Display->DisplayOffsetV);
23638  Utilities->SaveFileString(SessionFile, OutputLog1->Caption);
23639  Utilities->SaveFileString(SessionFile, OutputLog2->Caption);
23640  Utilities->SaveFileString(SessionFile, OutputLog3->Caption);
23641  Utilities->SaveFileString(SessionFile, OutputLog4->Caption);
23642  Utilities->SaveFileString(SessionFile, OutputLog5->Caption);
23643  Utilities->SaveFileString(SessionFile, OutputLog6->Caption);
23644  Utilities->SaveFileString(SessionFile, OutputLog7->Caption);
23645  Utilities->SaveFileString(SessionFile, OutputLog8->Caption);
23646  Utilities->SaveFileString(SessionFile, OutputLog9->Caption);
23647  Utilities->SaveFileString(SessionFile, OutputLog10->Caption);
23648 
23670  // ExcessLCDownMins saved after ***Interface*** at v2.2.0 (omitted in error earlier)
23671  Utilities->CallLogPop(1211);
23672 }
23673 
23674 // ---------------------------------------------------------------------------
23675 void TInterface::LoadInterface(int Caller, std::ifstream &SessionFile)
23676 {
23677  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadInterface");
23678  AnsiString OpMode = Utilities->LoadFileString(SessionFile);
23679 
23680  if(OpMode == "PreStart")
23681  {
23683  }
23684  else
23685  {
23687  }
23688  RailwayTitle = Utilities->LoadFileString(SessionFile);
23689  SavedFileName = CurDir + "\\" + RAILWAY_DIR_NAME + "\\" + RailwayTitle + ".rly";
23690 
23691  TimetableTitle = Utilities->LoadFileString(SessionFile);
23692  PreferredRoute = Utilities->LoadFileBool(SessionFile);
23693  ConsecSignalsRoute = Utilities->LoadFileBool(SessionFile);
23694  AutoSigsFlag = Utilities->LoadFileBool(SessionFile);
23695  Display->DisplayOffsetH = Utilities->LoadFileInt(SessionFile);
23696  Display->DisplayOffsetV = Utilities->LoadFileInt(SessionFile);
23701  OutputLog1->Caption = Utilities->LoadFileString(SessionFile);
23702  OutputLog2->Caption = Utilities->LoadFileString(SessionFile);
23703  OutputLog3->Caption = Utilities->LoadFileString(SessionFile);
23704  OutputLog4->Caption = Utilities->LoadFileString(SessionFile);
23705  OutputLog5->Caption = Utilities->LoadFileString(SessionFile);
23706  OutputLog6->Caption = Utilities->LoadFileString(SessionFile);
23707  OutputLog7->Caption = Utilities->LoadFileString(SessionFile);
23708  OutputLog8->Caption = Utilities->LoadFileString(SessionFile);
23709  OutputLog9->Caption = Utilities->LoadFileString(SessionFile);
23710  OutputLog10->Caption = Utilities->LoadFileString(SessionFile);
23711 
23719  TrainController->LateDeps = Utilities->LoadFileInt(SessionFile);
23733  session_api_->dump(); // update session INI file //added at v2.10.0
23734  Utilities->CallLogPop(1212);
23735 }
23736 
23737 // ---------------------------------------------------------------------------
23738 
23739 bool TInterface::CheckInterface(int Caller, std::ifstream &SessionFile)
23740 {
23741  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckInterface");
23742 
23743  AnsiString OpMode = "";
23744 
23745  if(!Utilities->CheckAndReadFileString(SessionFile, OpMode))
23746  {
23747  Utilities->CallLogPop(1767);
23748  return(false);
23749  }
23750  else if((OpMode != "PreStart") && (OpMode != "NotPreStart"))
23751  {
23752  Utilities->CallLogPop(1768);
23753  return(false);
23754  }
23755  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile)) // RailwayTitle
23756  {
23757  Utilities->CallLogPop(1213);
23758  return(false);
23759  }
23760  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile)) // TimetableTitle
23761  {
23762  Utilities->CallLogPop(1214);
23763  return(false);
23764  }
23765  if(!Utilities->CheckFileBool(SessionFile))
23766  {
23767  Utilities->CallLogPop(1216);
23768  return(false);
23769  }
23770  if(!Utilities->CheckFileBool(SessionFile))
23771  {
23772  Utilities->CallLogPop(1217);
23773  return(false);
23774  }
23775  if(!Utilities->CheckFileBool(SessionFile))
23776  {
23777  Utilities->CallLogPop(1218);
23778  return(false);
23779  }
23780  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23781  {
23782  Utilities->CallLogPop(1409);
23783  return(false);
23784  }
23785  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23786  {
23787  Utilities->CallLogPop(1486);
23788  return(false);
23789  }
23790  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23791  {
23792  Utilities->CallLogPop(1487);
23793  return(false);
23794  }
23795  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23796  {
23797  Utilities->CallLogPop(1488);
23798  return(false);
23799  }
23800  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23801  {
23802  Utilities->CallLogPop(1489);
23803  return(false);
23804  }
23805  if(!Utilities->CheckFileInt(SessionFile, -1000000, 1000000))
23806  {
23807  Utilities->CallLogPop(1528);
23808  return(false);
23809  }
23810  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23811  {
23812  Utilities->CallLogPop(1725);
23813  return(false);
23814  }
23815  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23816  {
23817  Utilities->CallLogPop(1726);
23818  return(false);
23819  }
23820  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23821  {
23822  Utilities->CallLogPop(1727);
23823  return(false);
23824  }
23825  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23826  {
23827  Utilities->CallLogPop(1728);
23828  return(false);
23829  }
23830  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23831  {
23832  Utilities->CallLogPop(1730);
23833  return(false);
23834  }
23835  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23836  {
23837  Utilities->CallLogPop(1731);
23838  return(false);
23839  }
23840  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23841  {
23842  Utilities->CallLogPop(1732);
23843  return(false);
23844  }
23845  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23846  {
23847  Utilities->CallLogPop(1733);
23848  return(false);
23849  }
23850  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23851  {
23852  Utilities->CallLogPop(1734);
23853  return(false);
23854  }
23855  if(!Utilities->CheckFileStringZeroDelimiter(SessionFile))
23856  {
23857  Utilities->CallLogPop(1789);
23858  return(false);
23859  }
23860  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23861  {
23862  Utilities->CallLogPop(1737);
23863  return(false);
23864  }
23865  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23866  {
23867  Utilities->CallLogPop(1738);
23868  return(false);
23869  }
23870  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23871  {
23872  Utilities->CallLogPop(1739);
23873  return(false);
23874  }
23875  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23876  {
23877  Utilities->CallLogPop(1740);
23878  return(false);
23879  }
23880  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23881  {
23882  Utilities->CallLogPop(1741);
23883  return(false);
23884  }
23885  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23886  {
23887  Utilities->CallLogPop(1742);
23888  return(false);
23889  }
23890  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23891  {
23892  Utilities->CallLogPop(1743);
23893  return(false);
23894  }
23895  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23896  {
23897  Utilities->CallLogPop(1744);
23898  return(false);
23899  }
23900  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23901  {
23902  Utilities->CallLogPop(1745);
23903  return(false);
23904  }
23905  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23906  {
23907  Utilities->CallLogPop(1746);
23908  return(false);
23909  }
23910  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23911  {
23912  Utilities->CallLogPop(1747);
23913  return(false);
23914  }
23915  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23916  {
23917  Utilities->CallLogPop(1748);
23918  return(false);
23919  }
23920  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23921  {
23922  Utilities->CallLogPop(1749);
23923  return(false);
23924  }
23925  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23926  {
23927  Utilities->CallLogPop(1750);
23928  return(false);
23929  }
23930  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23931  {
23932  Utilities->CallLogPop(1751);
23933  return(false);
23934  }
23935  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
23936  {
23937  Utilities->CallLogPop(1752);
23938  return(false);
23939  }
23940  if(!Utilities->CheckFileDouble(SessionFile))
23941  {
23942  Utilities->CallLogPop(1753);
23943  return(false);
23944  }
23945  if(!Utilities->CheckFileDouble(SessionFile))
23946  {
23947  Utilities->CallLogPop(1754);
23948  return(false);
23949  }
23950  if(!Utilities->CheckFileDouble(SessionFile))
23951  {
23952  Utilities->CallLogPop(1755);
23953  return(false);
23954  }
23955  if(!Utilities->CheckFileDouble(SessionFile))
23956  {
23957  Utilities->CallLogPop(1756);
23958  return(false);
23959  }
23960  if(!Utilities->CheckFileDouble(SessionFile))
23961  {
23962  Utilities->CallLogPop(1757);
23963  return(false);
23964  }
23965  Utilities->CallLogPop(1219);
23966  return(true);
23967 }
23968 
23969 // ---------------------------------------------------------------------------
23970 
23971 bool TInterface::SaveTimetableToSessionFile(int Caller, std::ofstream &SessionFile, AnsiString SessionFileStr)
23972 {
23973  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTimetableToSessionFile," + SessionFileStr);
23974  if(!FileExists(TempTTFileName))
23975  {
23976  Utilities->CallLogPop(1862);
23977  return(false);
23978  }
23979  SessionFile.close(); // close & re-open in append & binary mode so LF characters copy as LFs & not CRLFs
23980  SessionFile.open(SessionFileStr.c_str(), std::ios_base::app | std::ios_base::binary); // file pointer set to end
23981 
23982  int Handle = FileOpen(TempTTFileName, fmOpenRead);
23983  int Count = 0;
23984 
23985  while(Handle < 0) // this type of file use failed when used in SaveTempTimetableFile when used to resave timetable.tmp from
23986  // temp .ttb file, but changed that to avoid so many rapid file actions in quick succession & been OK since
23987  // then, but nevertheless have 10 retries before giving message to be on safe side
23988  {
23989  Handle = FileOpen(TempTTFileName, fmOpenRead);
23990  Count++;
23991  Delay(1, 50); // 50mSec delay between tries
23992  if(Count > 10)
23993  {
23994  ShowMessage("Failed to open temporary timetable file. Unable to save the session");
23995  Utilities->CallLogPop(1221);
23996  return(false);
23997  }
23998  }
23999 
24000  char *Buffer = new char[10000];
24001  int BytesRead;
24002 
24003  while(true)
24004  {
24005  BytesRead = FileRead(Handle, Buffer, 10000);
24006  SessionFile.write(Buffer, BytesRead);
24007  if(BytesRead < 10000)
24008  {
24009  break;
24010  }
24011  }
24012  delete[]Buffer;
24013  FileClose(Handle);
24014 
24015  SessionFile.close(); // close & re-open in append & text out mode as before so can write text
24016  SessionFile.open(SessionFileStr.c_str(), std::ios_base::app | std::ios_base::out); // file pointer set to end
24017 
24018  Utilities->SaveFileString(SessionFile, "***End***"); // marker for end of timetable
24019 // now need to save the TrainOperatingData so can be loaded back into the timetable as is
24020  Utilities->SaveFileInt(SessionFile, TrainController->TrainDataVector.size());
24021  for(unsigned int x = 0; x < TrainController->TrainDataVector.size(); x++)
24022  {
24023  Utilities->SaveFileInt(SessionFile, TrainController->TrainDataVector.at(x).TrainOperatingDataVector.size());
24024  for(unsigned int y = 0; y < TrainController->TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
24025  {
24026  Utilities->SaveFileInt(SessionFile, TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).TrainID);
24027  Utilities->SaveFileInt(SessionFile, (short)(TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).EventReported));
24028  Utilities->SaveFileInt(SessionFile, (short)(TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).RunningEntry));
24029  }
24030  }
24031  Utilities->CallLogPop(1220);
24032  return(true);
24033 }
24034 
24035 // ---------------------------------------------------------------------------
24036 
24037 bool TInterface::SaveTimetableToErrorFile(int Caller, std::ofstream &ErrorFile, AnsiString ErrorFileStr, AnsiString TimetableFileName)
24038 {
24039  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTimetableToErrorFile," + ErrorFileStr + "," + TimetableFileName);
24040  if(!FileExists(TimetableFileName))
24041  {
24042  Utilities->CallLogPop(1863);
24043  return(false);
24044  }
24045  ErrorFile.close(); // close & re-open in append & binary mode so LF characters copy as LFs & not CRLFs
24046  ErrorFile.open(ErrorFileStr.c_str(), std::ios_base::app | std::ios_base::binary); // file pointer set to end
24047 
24048  int Handle = FileOpen(TimetableFileName, fmOpenRead);
24049  int Count = 0;
24050 
24051  while(Handle < 0) // this type of file use failed when used in SaveTempTimetableFile when used to resave timetable.tmp from
24052  // temp .ttb file, but changed that to avoid so many rapid file actions in quick succession & been OK since
24053  // then, but nevertheless have 10 retries before giving message to be on safe side
24054  {
24055  Handle = FileOpen(TimetableFileName, fmOpenRead);
24056  Count++;
24057  Delay(5, 50); // 50mSec delay between tries
24058  if(Count > 10)
24059  {
24060  Utilities->CallLogPop(1835);
24061  return(false);
24062  }
24063  }
24064 
24065  char *Buffer = new char[10000];
24066  int BytesRead;
24067 
24068  while(true)
24069  {
24070  BytesRead = FileRead(Handle, Buffer, 10000);
24071  ErrorFile.write(Buffer, BytesRead);
24072  if(BytesRead < 10000)
24073  {
24074  break;
24075  }
24076  }
24077  delete[]Buffer;
24078  FileClose(Handle);
24079 
24080  ErrorFile.close(); // close & re-open in append & text out mode as before so can write text
24081  ErrorFile.open(ErrorFileStr.c_str(), std::ios_base::app | std::ios_base::out); // file pointer set to end
24082 
24083  Utilities->SaveFileString(ErrorFile, "***End of timetable***"); // marker for end of timetable
24084  Utilities->CallLogPop(1836);
24085  return(true);
24086 }
24087 
24088 // ---------------------------------------------------------------------------
24089 
24090 bool TInterface::LoadTimetableFromSessionFile(int Caller, std::ifstream &SessionFile)
24091 // the .ttb section is delimited by "***End***"
24092 // create the temporary timetable file in the working folder exactly like the original
24093 {
24094  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadTimetableFromSessionFile");
24095  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
24096  TrainController->SSHigh = false;
24097 // TrainController->MRSHigh = false; removed at v2.21.0
24098 // TrainController->MRSLow = false;
24099  TrainController->MassHigh = false;
24100  TrainController->BFHigh = false;
24101  TrainController->BFLow = false;
24102  TrainController->PwrHigh = false;
24103  TrainController->SigSHigh = false;
24104  TrainController->SigSLow = false;
24105  if((TempTTFileName != "") && FileExists(TempTTFileName))
24106  {
24107  DeleteFile(TempTTFileName);
24108  }
24109  int TempTTFileNumber = 0;
24110 
24111  while(FileExists(CurDir + "\\TmpTT" + AnsiString(TempTTFileNumber) + ".tmp"))
24112  {
24113  TempTTFileNumber++;
24114  }
24115  TempTTFileName = CurDir + "\\TmpTT" + AnsiString(TempTTFileNumber) + ".tmp";
24116 
24117  std::ofstream TTBFile(TempTTFileName.c_str()); // use text mode as SessionFile is in text mode, so CRLFs read as LFs, and LFs write as CRLFs.
24118  int Count;
24119  char Zero = '\0';
24120 
24121  if(!TTBFile.fail())
24122  {
24123  char *Buffer = new char[10000]; // can't use LoadFileString as that expects a '\0' delimiter
24124  char TempChar = (char)(SessionFile.peek()); // have a look at the next character, if it's '\n'
24125  if(TempChar == '\n')
24126  {
24127  SessionFile.get(TempChar); // then get rid of it, else leave it in as part of the first line
24128  }
24129  if(!SessionFile.getline(Buffer, 10000, '\0'))
24130  {
24131  TTBFile.close();
24132  DeleteFile(TempTTFileName);
24133  delete[]Buffer;
24134  Utilities->CallLogPop(1222);
24135  return(false);
24136  }
24137  Count = 0;
24138  for(int x = 0; x < 10000; x++)
24139  {
24140  if(Buffer[x] != '\0')
24141  {
24142  Count++;
24143  }
24144  else
24145  {
24146  break;
24147  }
24148  }
24149  while(AnsiString(Buffer) != "***End***")
24150  {
24151  TTBFile.write(Buffer, Count);
24152  TTBFile.write(&Zero, 1);
24153 // TTBFile.write(&NewLine, 1);
24154  if(!SessionFile.getline(Buffer, 10000, '\0'))
24155  {
24156  TTBFile.close();
24157  DeleteFile(TempTTFileName);
24158  delete[]Buffer;
24159  Utilities->CallLogPop(1223);
24160  return(false);
24161  }
24162  Count = 0;
24163  for(int x = 0; x < 10000; x++)
24164  {
24165  if(Buffer[x] != '\0')
24166  {
24167  Count++;
24168  }
24169  else
24170  {
24171  break;
24172  }
24173  }
24174  }
24175  TTBFile.close();
24176  delete[]Buffer;
24177 // SaveTempTimetableFile(1, TTBFileName); no need, already has required name
24178 // now create the internal timetable from the .tmp file
24179  bool GiveMessagesFalse = false;
24180  bool CheckLocationsExistInRailwayTrue = true;
24181  if(TrainController->TimetableIntegrityCheck(1, TempTTFileName.c_str(), GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
24182  {
24183  std::ifstream TTBLFile(TempTTFileName.c_str(), std::ios_base::binary);
24184  if(TTBLFile.is_open())
24185  {
24186  bool SessionFileTrue = true;
24187  if(!(BuildTrainDataVectorForLoadFile(1, TTBLFile, GiveMessagesFalse, CheckLocationsExistInRailwayTrue, SessionFileTrue)))
24188  {
24189  TTBLFile.close();
24190  DeleteFile(TempTTFileName);
24191  Utilities->CallLogPop(1224);
24192  return(false);
24193  }
24194  }
24195  else
24196  {
24197  DeleteFile(TempTTFileName);
24198  Utilities->CallLogPop(1225);
24199  return(false);
24200  }
24201  } // if(FileIntegrityCheck(TTBFileName.c_str()))
24202  else
24203  {
24204  DeleteFile(TempTTFileName);
24205  Utilities->CallLogPop(1226);
24206  return(false);
24207  }
24208 // DeleteFile(TempTTFileName); no, need to save it for later session saves
24209 
24210  // now need to load the TrainOperatingData so can be loaded back into the timetable
24211  int NumberOfTrainEntries = Utilities->LoadFileInt(SessionFile);
24212  if(NumberOfTrainEntries != (int)(TrainController->TrainDataVector.size()))
24213  {
24214  Utilities->CallLogPop(1811);
24215  return(false);
24216  }
24217  for(int x = 0; x < NumberOfTrainEntries; x++)
24218  {
24219  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
24220  if(NumberOfTrains != (int)(TrainController->TrainDataVector.at(x).TrainOperatingDataVector.size()))
24221  {
24222  Utilities->CallLogPop(1812);
24223  return(false);
24224  }
24225  for(int y = 0; y < NumberOfTrains; y++)
24226  {
24227  TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).TrainID = Utilities->LoadFileInt(SessionFile);
24228  TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).EventReported = (TActionEventType)(Utilities->LoadFileInt(SessionFile));
24229  TrainController->TrainDataVector.at(x).TrainOperatingDataVector.at(y).RunningEntry = (TRunningEntry)(Utilities->LoadFileInt(SessionFile));
24230  }
24231  }
24232  Utilities->CallLogPop(1227);
24233  return(true);
24234  }
24235  else
24236  {
24237  Utilities->CallLogPop(1228);
24238  return(false);
24239  }
24240 }
24241 
24242 // ---------------------------------------------------------------------------
24243 
24244 bool TInterface::CheckTimetableFromSessionFile(int Caller, std::ifstream &SessionFile)
24245 // the .ttb section is delimited by '\0' followed by "***End***" & has been saved in binary mode, i.e. no '\0'
24246 // string delimiters between entries, this check function just checks the (non-zero terminated) string entries in the file without
24247 // trying to build a timetable - that's done during load
24248 {
24249  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimetableFromSessionFile");
24250  AnsiString OutString;
24251 
24252  if(!Utilities->CheckAndReadFileString(SessionFile, OutString))
24253  {
24254  Utilities->CallLogPop(1229);
24255  return(false);
24256  }
24257  while(OutString != "***End***")
24258  {
24259  if(!Utilities->CheckAndReadFileString(SessionFile, OutString))
24260  {
24261  Utilities->CallLogPop(1230);
24262  return(false);
24263  }
24264  }
24265 // now need to check the TrainOperatingData, which was saved in text mode
24266  if(SessionFile.fail())
24267  {
24268  Utilities->CallLogPop(1231);
24269  return(false);
24270  }
24271  int NumberOfTrainEntries;
24272 
24273  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 100000, NumberOfTrainEntries)) //increased from 10000 to 100000 at v2.21.0 because of JasonB's WCML which
24274  { //reached 9346 in the middle of day 2 out of a 4 day timetable
24275  Utilities->CallLogPop(1232);
24276  return(false);
24277  }
24278  for(int x = 0; x < NumberOfTrainEntries; x++)
24279  {
24280  int NumberOfTrains;
24281  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, NumberOfTrains))
24282  {
24283  Utilities->CallLogPop(1233);
24284  return(false);
24285  }
24286  for(int y = 0; y < NumberOfTrains; y++)
24287  {
24288  if(!Utilities->CheckFileInt(SessionFile, -1, 1000000)) // TrainID
24289  {
24290  Utilities->CallLogPop(1234);
24291  return(false);
24292  }
24293  if(!Utilities->CheckFileInt(SessionFile, 0, 36)) // EventReported //increased to 32 at v2.11.1 due to Xeon error report 07/01/22 as there are 33
24294  //event reports now (increased at v2.9.1)
24295  { //increased again to 35 at v2.20.3 as there are now (at v2.20.3) 36 events in all
24296  //increased again to 36 at v2.21.0 for FailMissedCMS
24297  Utilities->CallLogPop(1235);
24298  return(false);
24299  }
24300  if(!Utilities->CheckFileInt(SessionFile, 0, 2)) // RunningEntry
24301  {
24302  Utilities->CallLogPop(1236);
24303  return(false);
24304  }
24305  }
24306  }
24307  Utilities->CallLogPop(1237);
24308  return(true);
24309 }
24310 
24311 // ---------------------------------------------------------------------------
24312 
24313 bool TInterface::BuildTrainDataVectorForLoadFile(int Caller, std::ifstream &TTBLFile, bool GiveMessages, bool CheckLocationsExistInRailway, bool SessionFile)
24314 {
24315  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildTrainDataVectorForLoadFile," + AnsiString((short)GiveMessages));
24316  TrainController->TrainDataVector.clear(); // get rid of any earlier timetable
24317  bool EndOfFile = false;
24318  int Count = 0;
24319  char *TrainTimetableString = new char[10000]; // enough for ~ 200 stations at 50 chars/station, should be adequate!
24320 
24321  while(!EndOfFile)
24322  {
24323  TTBLFile.getline(TrainTimetableString, 10000, '\0');
24324  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // stores a null in 1st position if doesn't load any characters
24325  {
24326  // may still have eof even if read a line (no CRLF at end), and
24327  // if so need to process it
24328  EndOfFile = true;
24329  break;
24330  }
24331  AnsiString OneLine(TrainTimetableString);
24332  bool FinalCallTrue = true;
24333  while((Count == 0) && !TrainController->ProcessOneTimetableLine(3, Count, OneLine, EndOfFile, FinalCallTrue, GiveMessages,
24334  CheckLocationsExistInRailway)) // get rid of lines before the start time
24335  {
24336  TTBLFile.getline(TrainTimetableString, 10000, '\0');
24337  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // see above
24338  {
24339  TTBLFile.close();
24340  throw Exception("Timetable FinalCall error - no start time on own line, Count = 0");
24341  }
24342  OneLine = AnsiString(TrainTimetableString);
24343  }
24344  // here when have accepted the start time
24345  if(Count == 0)
24346  {
24347  Count++; // increment past the start time
24348  TTBLFile.getline(TrainTimetableString, 10000, '\0'); // get next line after start time
24349  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // see above
24350  {
24351  EndOfFile = true;
24352  OneLine = "";
24353  }
24354  else
24355  {
24356  OneLine = AnsiString(TrainTimetableString);
24357  }
24358  }
24359  if(!TrainController->ProcessOneTimetableLine(4, Count, OneLine, EndOfFile, FinalCallTrue, GiveMessages, CheckLocationsExistInRailway))
24360  {
24361  TTBLFile.close();
24362  throw Exception("Timetable FinalCall error in processing one timetable line, Count = " + AnsiString(Count));
24363  }
24364  if(EndOfFile && (Count < 2)) // Timetable must contain at least two relevant lines, one for start time and at least one train
24365  {
24366  TTBLFile.close();
24367  throw Exception("Timetable FinalCall error - too few or no relevant entries, Count = " + AnsiString(Count));
24368  }
24369  Count++;
24370  }
24371  TTBLFile.close();
24372  delete[]TrainTimetableString;
24373  bool TwoLocationFlag; //not used in LoadFile
24374 // here when first pass actions completed successfully
24375  if(!TrainController->SecondPassActions(0, GiveMessages, TwoLocationFlag)) // Check for matching join/split HeadCodes, check increasing times & matching split/join
24376  // times, complete arrival & departure times for each TT line, check & complete train info for each type of start,
24377  // messages given in function if errors & vector cleared
24378  {
24379  if(GiveMessages)
24380  {
24381  ShowMessage("Timetable secondary integrity check failed - unable to load.\n\nPlease make sure that the timetable has been validated and that you are using the latest program version.");
24382  }
24383  Utilities->CallLogPop(1238);
24384  return(false);
24385  }
24386  else
24387  {
24388 // TimetableLoaded = true;
24389  if(!SessionFile) // new timetable being loaded so need new TimetableTitle, if SessionFile true then already have it from LoadInterface
24390  {
24391  for(int x = TimetableDialog->FileName.Length(); x > 0; x--)
24392  {
24393  if(TimetableDialog->FileName[x] == '\\')
24394  {
24395  TimetableTitle = TimetableDialog->FileName.SubString(x + 1, TimetableDialog->FileName.Length() - x - 4);
24396  SetCaption(4);
24397  break;
24398  }
24399  }
24400  }
24401 // if(GiveMessages) //only set BaseMode if have manually loaded a timetable changed for the below in v2.4.0
24402  if(!SessionFile) // only set BaseMode if have manually loaded a timetable, i.e SessionFile is false
24403  {
24404  Level1Mode = BaseMode;
24405  SetLevel1Mode(28);
24406  }
24407  }
24408  Utilities->CallLogPop(1239);
24409  return(true);
24410 }
24411 
24412 // ---------------------------------------------------------------------------
24413 
24414 bool TInterface::BuildTrainDataVectorForValidateFile(int Caller, std::ifstream &TTBLFile, bool GiveMessages, bool CheckLocationsExistInRailway)
24415 {
24416  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildTrainDataVectorForValidateFile," + AnsiString((short)GiveMessages));
24417  TrainController->TrainDataVector.clear(); // get rid of any earlier timetable
24418  bool EndOfFile = false;
24419  int Count = 0;
24420  char *TrainTimetableString = new char[10000]; // enough for ~ 200 stations at 50 chars/station, should be adequate!
24421 
24422  while(!EndOfFile)
24423  {
24424  TTBLFile.getline(TrainTimetableString, 10000, '\0');
24425  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // stores a null in 1st position if doesn't load any characters
24426  {
24427  // may still have eof even if read a line (no CRLF at end), and
24428  // if so need to process it
24429  EndOfFile = true;
24430  break;
24431  }
24432  AnsiString OneLine(TrainTimetableString);
24433  bool FinalCallTrue = true;
24434  while((Count == 0) && !TrainController->ProcessOneTimetableLine(0, Count, OneLine, EndOfFile, FinalCallTrue, GiveMessages,
24435  CheckLocationsExistInRailway)) // get rid of lines before the start time
24436  {
24437  TTBLFile.getline(TrainTimetableString, 10000, '\0');
24438  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // see above
24439  {
24440  TTBLFile.close();
24441  throw Exception("Timetable FinalCall error - no start time on own line, Count = 0");
24442  }
24443  OneLine = AnsiString(TrainTimetableString);
24444  }
24445  // here when have accepted the start time
24446  if(Count == 0)
24447  {
24448  Count++; // increment past the start time
24449  TTBLFile.getline(TrainTimetableString, 10000, '\0'); // get next line after start time
24450  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0')) // see above
24451  {
24452  EndOfFile = true;
24453  OneLine = "";
24454  }
24455  else
24456  {
24457  OneLine = AnsiString(TrainTimetableString);
24458  }
24459  }
24460  if(!TrainController->ProcessOneTimetableLine(1, Count, OneLine, EndOfFile, FinalCallTrue, GiveMessages, CheckLocationsExistInRailway))
24461  {
24462  TTBLFile.close();
24463  throw Exception("Timetable FinalCall error in processing one timetable line, Count = " + AnsiString(Count));
24464  }
24465  if(EndOfFile && (Count < 2)) // Timetable must contain at least two relevant lines, one for start time and at least one train
24466  {
24467  TTBLFile.close();
24468  throw Exception("Timetable FinalCall error - too few or no relevant entries, Count = " + AnsiString(Count));
24469  }
24470  Count++;
24471  }
24472  TTBLFile.close();
24473  delete[]TrainTimetableString;
24474  bool TwoLocationFlag;
24475 // here when first pass actions completed successfully
24476  if(!TrainController->SecondPassActions(1, GiveMessages, TwoLocationFlag)) // Check for matching join/split HeadCodes, check increasing times & matching split/join
24477  // times, complete arrival & departure times for each TT line, check & complete train info for each type of start,
24478  // messages given in function if errors & vector cleared
24479  {
24480 // if(GiveMessages) ShowMessage("Timetable secondary integrity check failed");
24481 // above dropped in v2.4.0 as all called functions give own messages
24482  Utilities->CallLogPop(1665);
24483  return(false);
24484  }
24485  else if(TwoLocationFlag) //i.e returns true but have services with two or more locations without a cdt between //added at v2.9.1
24486  {
24487  AnsiString AllServices = "", Suffix = "";
24488  int Count = 1;
24490  {
24491  TwoLocationNamePanel->Top = TimetableEditPanel->Top + ((TimetableEditPanel->Height - TwoLocationNamePanel->Height) / 2);
24492  TwoLocationNamePanel->Left = TimetableEditPanel->Left + ((TimetableEditPanel->Width - TwoLocationNamePanel->Width) / 2);
24493  for(TTrainController::TServiceCallingLocsList::iterator TLLIt = TrainController->TwoLocationList.begin(); TLLIt != TrainController->TwoLocationList.end(); TLLIt++)
24494  {
24495  if(Count <= 5)
24496  {
24497  AllServices = AllServices + *TLLIt + ", ";
24498  }
24499  Count++;
24500  }
24501  if(Count > 6) //if exactly 5 entered then Count will be 6, Count always 1 more than entered
24502  {
24503  Suffix = "(more than 5, first 5 are) ";
24504  }
24505  if(Count > 2) //more than 1
24506  {
24507  TwoLocationNameLabel->Caption = "The following services have two or more locations with the same name without a change of\n"
24508  "direction between them. If this isn't intended then please correct them.\n\n"
24509  "Before ticking the check box please be sure that all services are correct.\n\n"
24510  "Services: " + Suffix + " " + AllServices;
24511  }
24512  else
24513  {
24514  TwoLocationNameLabel->Caption = "The following service has two or more locations with the same name without a change of\n"
24515  "direction between them. If this isn't intended then please correct it.\n\n"
24516  "Service: " + AllServices;
24517  }
24518  TwoLocationNamePanel->Visible = true;
24519  TwoLocationNamePanel->BringToFront();
24520  ShowHideTTButton->Enabled = false;
24521  ExitTTModeButton->Enabled = false;
24522  TimetableEditPanel->Enabled = false;
24523  }
24524  }
24525  Utilities->CallLogPop(1666);
24526  return(true);
24527 }
24528 
24529 // ---------------------------------------------------------------------------
24530 
24531 bool TInterface::SessionFileIntegrityCheck(int Caller, AnsiString FileName)
24532 /* Here need to check as far as timetable, then go back and load the track, because the timetable compilation check
24533 relies on the track vector and map being in place. Won't affect the later load because the vector and map are cleared
24534 before loading.
24535 
24536 Although this appears cumbersome, I initially used tellg() & seekg() to reposition the file pointer without having to do
24537 all this backtracking. However after many problems I eventually found that tellg() sometimes returns false values,
24538 which cause problems when reloaded using seekg(). This must be a compiler problem, though I could find no mention of it
24539 in the CBuilder4 issues list or on the web. The full write-up is at the end of this unit.
24540 
24541 Also, with hindsight, I wish I had just saved and reloaded the timetable vectors rather than the text of the timetable. That
24542 would probably have been easier. To change it now though would cause compatibility problems with sessions created by earlier versions.
24543 */
24544 {
24545  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SessionFileIntegrityCheck," + FileName);
24546  std::ifstream InFile(FileName.c_str());
24547 // first pass as far as timetable
24548  int NumberOfActiveElements;
24549  bool GraphicsFollow = false;
24550 
24551  if(InFile.is_open())
24552  {
24554  // expected to be "***Interface***" for original version or "Version + : ***Interface***" for later releases + ExcessLCDownMins added as float at v2.3.0
24555  {
24556  InFile.close();
24557  Utilities->CallLogPop(1240);
24558  return(false);
24559  }
24560  if(!CheckInterface(0, InFile))
24561  {
24562  InFile.close();
24563  Utilities->CallLogPop(1241);
24564  return(false);
24565  }
24566  // check track elements
24567  if(!Utilities->CheckAndCompareFileString(InFile, "***Track***"))
24568  {
24569  InFile.close();
24570  Utilities->CallLogPop(1242);
24571  return(false);
24572  }
24573  if(!Track->CheckTrackElementsInFile(2, NumberOfActiveElements, GraphicsFollow, InFile))
24574  {
24575  InFile.close();
24576  Utilities->CallLogPop(1243);
24577  return(false);
24578  }
24579  if(InFile.fail())
24580  {
24581  InFile.close();
24582  Utilities->CallLogPop(1244);
24583  return(false);
24584  }
24585  // check text elements
24586  if(!Utilities->CheckAndCompareFileString(InFile, "***Text***"))
24587  {
24588  InFile.close();
24589  Utilities->CallLogPop(1245);
24590  return(false);
24591  }
24592  if(!TextHandler->CheckTextElementsInFile(1, InFile))
24593  {
24594  InFile.close();
24595  Utilities->CallLogPop(1246);
24596  return(false);
24597  }
24598  if(InFile.fail())
24599  {
24600  InFile.close();
24601  Utilities->CallLogPop(1247);
24602  return(false);
24603  }
24604  // check PrefDir elements
24605  if(!Utilities->CheckAndCompareFileString(InFile, "***PrefDirs***"))
24606  {
24607  InFile.close();
24608  Utilities->CallLogPop(1248);
24609  return(false);
24610  }
24611  if(!EveryPrefDir->CheckOnePrefDir(1, NumberOfActiveElements, InFile))
24612  {
24613  InFile.close();
24614  Utilities->CallLogPop(1249);
24615  return(false);
24616  }
24617  if(InFile.fail())
24618  {
24619  InFile.close();
24620  Utilities->CallLogPop(1250);
24621  return(false);
24622  }
24623  // check graphics
24624  if(GraphicsFollow)
24625  {
24626  if(!Track->CheckUserGraphics(1, InFile, CurDir + "\\" + USERGRAPHICS_DIR_NAME)) // include path to Graphics folder
24627  {
24628  InFile.close();
24629  Utilities->CallLogPop(2187);
24630  return(false);
24631  }
24632  if(InFile.fail())
24633  {
24634  InFile.close();
24635  Utilities->CallLogPop(2188);
24636  return(false);
24637  }
24638  }
24639  // check routes
24640  if(!Utilities->CheckAndCompareFileString(InFile, "***Routes***"))
24641  {
24642  InFile.close();
24643  Utilities->CallLogPop(1251);
24644  return(false);
24645  }
24646  if(!AllRoutes->CheckRoutes(0, NumberOfActiveElements, InFile))
24647  {
24648  InFile.close();
24649  Utilities->CallLogPop(1252);
24650  return(false);
24651  }
24652  if(InFile.fail())
24653  {
24654  InFile.close();
24655  Utilities->CallLogPop(1253);
24656  return(false);
24657  }
24658  // check LockedRoutes
24659  if(!Utilities->CheckAndCompareFileString(InFile, "***Locked routes***"))
24660  {
24661  InFile.close();
24662  Utilities->CallLogPop(1254);
24663  return(false);
24664  }
24665  if(!TrainController->CheckSessionLockedRoutes(0, InFile))
24666  {
24667  InFile.close();
24668  Utilities->CallLogPop(1255);
24669  return(false);
24670  }
24671  if(InFile.fail())
24672  {
24673  InFile.close();
24674  Utilities->CallLogPop(1256);
24675  return(false);
24676  }
24677  // check ContinuationAutoSigs
24678  if(!Utilities->CheckAndCompareFileString(InFile, "***ContinuationAutoSigEntries***"))
24679  {
24680  InFile.close();
24681  Utilities->CallLogPop(1257);
24682  return(false);
24683  }
24685  {
24686  InFile.close();
24687  Utilities->CallLogPop(1258);
24688  return(false);
24689  }
24690  if(InFile.fail())
24691  {
24692  InFile.close();
24693  Utilities->CallLogPop(1259);
24694  return(false);
24695  }
24696  // check BarriersDownVector, but ensure backwards compatibility with earlier files which don't have this section
24697  AnsiString TempString = Utilities->LoadFileString(InFile);
24698  if((TempString != "***BarriersDownVector***") && (TempString != "***Timetable***"))
24699  {
24700  InFile.close();
24701  Utilities->CallLogPop(1964);
24702  return(false);
24703  }
24704  if(TempString == "***BarriersDownVector***")
24705  {
24706  if(!Track->CheckActiveLCVector(0, InFile))
24707  {
24708  InFile.close();
24709  Utilities->CallLogPop(1965);
24710  return(false);
24711  }
24712  if(InFile.fail())
24713  {
24714  InFile.close();
24715  Utilities->CallLogPop(1966);
24716  return(false);
24717  }
24718  if(!Utilities->CheckAndCompareFileString(InFile, "***Timetable***"))
24719  {
24720  InFile.close();
24721  Utilities->CallLogPop(1260);
24722  return(false);
24723  }
24724  }
24725  // check timetable (marker string already checked immediately above)
24726  if(!CheckTimetableFromSessionFile(0, InFile))
24727  {
24728  InFile.close();
24729  Utilities->CallLogPop(1261);
24730  return(false);
24731  }
24732  if(InFile.fail())
24733  {
24734  InFile.close();
24735  Utilities->CallLogPop(1262);
24736  return(false);
24737  }
24738  }
24739  else
24740  {
24741  InFile.close();
24742 // ShowMessage("Session file failed to open, reason not known, unable to load session."); message given in calling function if returns false
24743  Utilities->CallLogPop(1263);
24744  return(false);
24745  }
24746 // now ready for the 2nd pass for timetable loading and checking
24747  InFile.close();
24748  InFile.open(FileName.c_str());
24749  if(InFile.is_open())
24750  {
24752  {
24753  InFile.close();
24754  Utilities->CallLogPop(1264);
24755  return(false);
24756  }
24757  if(!CheckInterface(1, InFile))
24758  {
24759  InFile.close();
24760  Utilities->CallLogPop(1265);
24761  return(false);
24762  }
24763  // load track elements
24764  if(!Utilities->CheckAndCompareFileString(InFile, "***Track***"))
24765  {
24766  InFile.close();
24767  Utilities->CallLogPop(1266);
24768  return(false);
24769  }
24770  bool GraphicsFollow = false;
24771  Track->LoadTrack(2, InFile, GraphicsFollow); // load the track this time
24772  if(InFile.fail())
24773  {
24774  InFile.close();
24775  Utilities->CallLogPop(1267);
24776  return(false);
24777  }
24778  // check text elements
24779  if(!Utilities->CheckAndCompareFileString(InFile, "***Text***"))
24780  {
24781  InFile.close();
24782  Utilities->CallLogPop(1268);
24783  return(false);
24784  }
24785  if(!TextHandler->CheckTextElementsInFile(2, InFile))
24786  {
24787  InFile.close();
24788  Utilities->CallLogPop(1269);
24789  return(false);
24790  }
24791  if(InFile.fail())
24792  {
24793  InFile.close();
24794  Utilities->CallLogPop(1270);
24795  return(false);
24796  }
24797  // check PrefDir elements
24798  if(!Utilities->CheckAndCompareFileString(InFile, "***PrefDirs***"))
24799  {
24800  InFile.close();
24801  Utilities->CallLogPop(1271);
24802  return(false);
24803  }
24804  if(!EveryPrefDir->CheckOnePrefDir(2, NumberOfActiveElements, InFile))
24805  {
24806  InFile.close();
24807  Utilities->CallLogPop(1272);
24808  return(false);
24809  }
24810  if(InFile.fail())
24811  {
24812  InFile.close();
24813  Utilities->CallLogPop(1273);
24814  return(false);
24815  }
24816  // check graphics
24817  if(GraphicsFollow)
24818  {
24819  if(!Track->CheckUserGraphics(2, InFile, CurDir + "\\" + USERGRAPHICS_DIR_NAME)) // include path to Graphics folder
24820  {
24821  InFile.close();
24822  Utilities->CallLogPop(2189);
24823  return(false);
24824  }
24825  if(InFile.fail())
24826  {
24827  InFile.close();
24828  Utilities->CallLogPop(2190);
24829  return(false);
24830  }
24831  }
24832  // check routes
24833  if(!Utilities->CheckAndCompareFileString(InFile, "***Routes***"))
24834  {
24835  InFile.close();
24836  Utilities->CallLogPop(1274);
24837  return(false);
24838  }
24839  if(!AllRoutes->CheckRoutes(1, NumberOfActiveElements, InFile))
24840  {
24841  InFile.close();
24842  Utilities->CallLogPop(1275);
24843  return(false);
24844  }
24845  if(InFile.fail())
24846  {
24847  InFile.close();
24848  Utilities->CallLogPop(1276);
24849  return(false);
24850  }
24851  // check LockedRoutes
24852  if(!Utilities->CheckAndCompareFileString(InFile, "***Locked routes***"))
24853  {
24854  InFile.close();
24855  Utilities->CallLogPop(1277);
24856  return(false);
24857  }
24858  if(!TrainController->CheckSessionLockedRoutes(1, InFile))
24859  {
24860  InFile.close();
24861  Utilities->CallLogPop(1278);
24862  return(false);
24863  }
24864  if(InFile.fail())
24865  {
24866  InFile.close();
24867  Utilities->CallLogPop(1279);
24868  return(false);
24869  }
24870  // check ContinuationAutoSigs
24871  if(!Utilities->CheckAndCompareFileString(InFile, "***ContinuationAutoSigEntries***"))
24872  {
24873  InFile.close();
24874  Utilities->CallLogPop(1280);
24875  return(false);
24876  }
24878  {
24879  InFile.close();
24880  Utilities->CallLogPop(1281);
24881  return(false);
24882  }
24883  if(InFile.fail())
24884  {
24885  InFile.close();
24886  Utilities->CallLogPop(1282);
24887  return(false);
24888  }
24889  // check BarriersDownVector, but ensure backwards compatibility with earlier files which don't have this section
24890  AnsiString TempString = Utilities->LoadFileString(InFile);
24891  if((TempString != "***BarriersDownVector***") && (TempString != "***Timetable***"))
24892  {
24893  InFile.close();
24894  Utilities->CallLogPop(1967);
24895  return(false);
24896  }
24897  if(TempString == "***BarriersDownVector***")
24898  {
24899  if(!Track->CheckActiveLCVector(0, InFile))
24900  {
24901  InFile.close();
24902  Utilities->CallLogPop(1968);
24903  return(false);
24904  }
24905  if(InFile.fail())
24906  {
24907  InFile.close();
24908  Utilities->CallLogPop(1969);
24909  return(false);
24910  }
24911  if(!Utilities->CheckAndCompareFileString(InFile, "***Timetable***"))
24912  {
24913  InFile.close();
24914  Utilities->CallLogPop(1283);
24915  return(false);
24916  }
24917  }
24918  // check timetable (marker string already checked)
24919  if(!LoadTimetableFromSessionFile(1, InFile))
24920  {
24921  InFile.close();
24922  Utilities->CallLogPop(1284);
24923  return(false);
24924  }
24925  if(InFile.fail())
24926  {
24927  InFile.close();
24928  Utilities->CallLogPop(1285);
24929  return(false);
24930  }
24931  // check timetable clock
24932  if(!Utilities->CheckAndCompareFileString(InFile, "***TimetableClock***"))
24933  {
24934  InFile.close();
24935  Utilities->CallLogPop(1286);
24936  return(false);
24937  }
24938  if(!Utilities->CheckFileDouble(InFile))
24939  {
24940  InFile.close();
24941  Utilities->CallLogPop(1287);
24942  return(false);
24943  }
24944  // check trains
24945  if(!Utilities->CheckAndCompareFileString(InFile, "***Trains***"))
24946  {
24947  InFile.close();
24948  Utilities->CallLogPop(1288);
24949  return(false);
24950  }
24951  if(!TrainController->CheckSessionTrains(0, InFile))
24952  {
24953  InFile.close();
24954  Utilities->CallLogPop(1289);
24955  return(false);
24956  }
24957  if(InFile.fail())
24958  {
24959  InFile.close();
24960  Utilities->CallLogPop(1290);
24961  return(false);
24962  }
24963  if(!Utilities->CheckAndCompareFileString(InFile, "***Performance file***"))
24964  {
24965  InFile.close();
24966  Utilities->CallLogPop(1291);
24967  return(false);
24968  }
24969  if(!CheckPerformanceFile(0, InFile))
24970  {
24971  InFile.close();
24972  Utilities->CallLogPop(1292);
24973  return(false);
24974  }
24975  char TempChar;
24976  InFile.get(TempChar);
24977  while(!InFile.eof() && ((TempChar == '\n') || (TempChar == '\0'))) // when emerge from here either have eof or '*'
24978  {
24979  InFile.get(TempChar);
24980  }
24981  if(!InFile.eof()) // additional checks needed
24982  {
24983  if(!Utilities->CheckFileString(InFile))
24984  {
24985  InFile.close();
24986  Utilities->CallLogPop(2198);
24987  return(false);
24988  }
24989  if(!Utilities->CheckFileInt(InFile, 0, 1000000)) // TrainController->AvHoursIntValue
24990  {
24991  InFile.close();
24992  Utilities->CallLogPop(2199);
24993  return(false);
24994  }
24995  if(!Utilities->CheckFileInt(InFile, 0, 1000000)) // number of train failures
24996  {
24997  InFile.close();
24998  Utilities->CallLogPop(2200);
24999  return(false);
25000  }
25001  // now check any failed trains along with their OriginalPowerAtRail values
25002  Utilities->CheckFileString(InFile); // discard "***Failed Trains***"
25003  int IDVal;
25004  if(!Utilities->CheckAndReadFileInt(InFile, -1, 1000000, IDVal)) // train ID or -1 for no more failed trains,
25005  {
25006  InFile.close();
25007  Utilities->CallLogPop(2201);
25008  return(false);
25009  }
25010  double PowerDouble;
25011  while(IDVal != -1)
25012  {
25013  Utilities->CheckFileDouble(InFile); // original power
25014  if(!Utilities->CheckAndReadFileInt(InFile, -1, 1000000, IDVal))
25015  {
25016  InFile.close();
25017  Utilities->CallLogPop(2202);
25018  return(false);
25019  }
25020  }
25021  }
25022  InFile.close();
25023  }
25024  else
25025  {
25026  InFile.close();
25027  Utilities->CallLogPop(1293);
25028  return(false);
25029  }
25030  Utilities->CallLogPop(1294);
25031  return(true);
25032 }
25033 
25034 // ---------------------------------------------------------------------------
25035 
25036 void TInterface::LoadPerformanceFile(int Caller, std::ifstream &InFile)
25037 // Note that the file integrity has already been checked using CheckPerformanceFile
25038 {
25039  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadPerformanceFile");
25040  AnsiString TempString = "", Line1 = "", Line2 = "", Line3 = "", Line4 = "", Line5 = "";
25041  char *Buffer = new char[1000];
25042  char TempChar;
25043 
25044  InFile.get(TempChar); // '\n'
25045  InFile.getline(Buffer, 1000);
25046  TempString = AnsiString(Buffer);
25047  if(TempString == "***End of performance file***") //added at v2.10.0
25048  {
25049  PerfLogForm->PerformanceLog(17, "Performance Log\nRailway: " + RailwayTitle + "\nTimetable: " + TimetableTitle + "\nStart Time: " +
25050  TrainController->TimetableStartTime.FormatString("hh:nn"));
25051  }
25052  else
25053  {
25054  while(TempString != "***End of performance file***")
25055  {
25056  PerfLogForm->PerformanceLogBox->Lines->Add(TempString);
25057  Utilities->PerformanceFile << TempString.c_str() << '\n';
25058  Utilities->PerformanceFile.flush(); //added at v2.13.0
25059  InFile.getline(Buffer, 1000);
25060  TempString = AnsiString(Buffer);
25061  }
25062  }
25063  delete[] Buffer;
25064  Utilities->CallLogPop(1295);
25065 }
25066 
25067 // ---------------------------------------------------------------------------
25068 
25069 bool TInterface::CheckPerformanceFile(int Caller, std::ifstream &InFile)
25070 {
25071  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckPerformanceFile");
25072  AnsiString TempString = "";
25073  char TempChar;
25074 
25075  InFile.get(TempChar);
25076  if(TempChar != '\n')
25077  {
25078  Utilities->CallLogPop(1296);
25079  return(false);
25080  }
25081  if(!Utilities->CheckAndReadFileString(InFile, TempString))
25082  {
25083  Utilities->CallLogPop(1297);
25084  return(false);
25085  }
25086  while(TempString != "***End of performance file***")
25087  {
25088  if(!Utilities->CheckAndReadFileString(InFile, TempString))
25089  {
25090  Utilities->CallLogPop(1298);
25091  return(false);
25092  }
25093  }
25094  Utilities->CallLogPop(1299);
25095  return(true);
25096 }
25097 
25098 // ---------------------------------------------------------------------------
25099 
25100 void TInterface::SavePerformanceFile(int Caller, std::ofstream &OutFile)
25101 {
25102  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SavePerformanceFile");
25103  AnsiString Text = PerfLogForm->PerformanceLogBox->Text;
25104 
25105  while(Text != "")
25106  {
25107  AnsiString OneLine = Text.SubString(1, Text.Pos('\x0D'));
25108  while((OneLine.Length() > 0) && OneLine[OneLine.Length()] < ' ')
25109  {
25110  OneLine.SetLength(OneLine.Length() - 1); // get rid of trailing control characters
25111  }
25112  Text = Text.SubString(Text.Pos('\x0D'), Text.Length());
25113  while((Text.Length() > 0) && Text[1] < ' ')
25114  {
25115  Text = Text.SubString(2, (Text.Length() - 1)); // get rid of leading control characters
25116  }
25117  OutFile << OneLine.c_str() << '\n';
25118  }
25119  Utilities->CallLogPop(1300);
25120 }
25121 
25122 // ---------------------------------------------------------------------------
25123 
25125 {
25126  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetRouteButtonsInfoCaptionAndRouteNotStarted");
25127  if(EveryPrefDir->PrefDirSize() > 0)
25128  {
25130  {
25131  AutoSigsButton->Enabled = false;
25132  SigAutoNonConsecButton->Enabled = true;
25133  SigPrefConsecButton->Enabled = true;
25134  SigPrefNonConsecButton->Enabled = true;
25135  UnrestrictedButton->Enabled = true;
25136  InfoPanel->Visible = true;
25137  if(Level2OperMode == PreStart)
25138  {
25139  InfoPanel->Caption = "PRE-START: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
25140  }
25141  else
25142  {
25143  InfoPanel->Caption = "OPERATING: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
25144  }
25145  InfoCaptionStore = InfoPanel->Caption;
25146  }
25147  else if(AutoSigsFlag && !ConsecSignalsRoute) //added at v2.14.0
25148  {
25149  AutoSigsButton->Enabled = true;
25150  SigAutoNonConsecButton->Enabled = false;
25151  SigPrefConsecButton->Enabled = true;
25152  SigPrefNonConsecButton->Enabled = true;
25153  UnrestrictedButton->Enabled = true;
25154  InfoPanel->Visible = true;
25155  if(Level2OperMode == PreStart)
25156  {
25157  InfoPanel->Caption = "PRE-START: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
25158  }
25159  else
25160  {
25161  InfoPanel->Caption = "OPERATING: Select AUTOMATIC SIGNAL ROUTE start signal, or left click points to change manually";
25162  }
25163  InfoCaptionStore = InfoPanel->Caption;
25164  }
25165  else if(PreferredRoute && ConsecSignalsRoute) // added at v2.7.0, was just ConsecSignalsRoute
25166  {
25167  AutoSigsButton->Enabled = true;
25168  SigAutoNonConsecButton->Enabled = true;
25169  SigPrefConsecButton->Enabled = false;
25170  SigPrefNonConsecButton->Enabled = true;
25171  UnrestrictedButton->Enabled = true;
25172  InfoPanel->Visible = true;
25173  if(Level2OperMode == PreStart)
25174  {
25175  InfoPanel->Caption = "PRE-START: Select PREFERRED ROUTE start signal, or left click points to change manually";
25176  }
25177  else
25178  {
25179  InfoPanel->Caption = "OPERATING: Select PREFERRED ROUTE start signal, or left click points to change manually";
25180  }
25181  InfoCaptionStore = InfoPanel->Caption;
25182  }
25183  else if(PreferredRoute && !ConsecSignalsRoute) // added at v2.7.0
25184  {
25185  AutoSigsButton->Enabled = true;
25186  SigAutoNonConsecButton->Enabled = true;
25187  SigPrefConsecButton->Enabled = true;
25188  SigPrefNonConsecButton->Enabled = false;
25189  UnrestrictedButton->Enabled = true;
25190  InfoPanel->Visible = true;
25191  if(Level2OperMode == PreStart)
25192  {
25193  InfoPanel->Caption = "PRE-START: Select PREFERRED ROUTE start signal, or left click points to change manually";
25194  }
25195  else
25196  {
25197  InfoPanel->Caption = "OPERATING: Select PREFERRED ROUTE start signal, or left click points to change manually";
25198  }
25199  InfoCaptionStore = InfoPanel->Caption;
25200  }
25201  else //unrestricted route
25202  {
25203  AutoSigsButton->Enabled = true;
25204  SigAutoNonConsecButton->Enabled = true;
25205  SigPrefConsecButton->Enabled = true;
25206  SigPrefNonConsecButton->Enabled = true;
25207  UnrestrictedButton->Enabled = false;
25208  InfoPanel->Visible = true;
25209  if(Level2OperMode == PreStart)
25210  {
25211  InfoPanel->Caption = "PRE-START: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
25212  }
25213  else
25214  {
25215  InfoPanel->Caption = "OPERATING: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
25216  }
25217  InfoCaptionStore = InfoPanel->Caption;
25218  }
25219  }
25220  else
25221  {
25222  AutoSigsButton->Enabled = false;
25223  SigAutoNonConsecButton->Enabled = false;
25224  SigPrefConsecButton->Enabled = false;
25225  SigPrefNonConsecButton->Enabled = false;
25226  UnrestrictedButton->Enabled = false;
25227  InfoPanel->Visible = true;
25228  if(Level2OperMode == PreStart)
25229  {
25230  InfoPanel->Caption = "PRE-START: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
25231  }
25232  else
25233  {
25234  InfoPanel->Caption = "OPERATING: Select UNRESTRICTED ROUTE start location, or left click points to change manually";
25235  }
25236  InfoCaptionStore = InfoPanel->Caption;
25237  }
25239  {
25240  RouteCancelButton->Enabled = true;
25241  }
25242  else
25243  {
25244  RouteCancelButton->Enabled = false;
25245  }
25247  AutoRouteStartMarker->PlotOriginal(37, Display); // so start marker will replot if had selected start before pause & zoom
25250  Utilities->CallLogPop(1301);
25251 }
25252 
25253 // ---------------------------------------------------------------------------
25254 
25256 {
25257  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetPausedOrZoomedInfoCaption");
25258  if(Display->ZoomOutFlag)
25259  {
25260  InfoPanel->Visible = true;
25261  InfoPanel->Caption = "Left click screen to zoom in at that position";
25262  }
25263  else if(Level2OperMode == Paused)
25264  {
25265  InfoPanel->Visible = true;
25266  InfoPanel->Caption = "PAUSED: Railway state changes disabled";
25267  }
25268 // otherwise do nothing
25269  Utilities->CallLogPop(1302);
25270 }
25271 
25272 // ---------------------------------------------------------------------------
25273 
25275 {
25276  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DisableRouteButtons");
25277  RouteCancelButton->Enabled = false;
25278  AutoSigsButton->Enabled = false;
25279  SigAutoNonConsecButton->Enabled = false;
25280  SigPrefConsecButton->Enabled = false;
25281  SigPrefNonConsecButton->Enabled = false;
25282  UnrestrictedButton->Enabled = false;
25283  Utilities->CallLogPop(1303);
25284 }
25285 
25286 // ---------------------------------------------------------------------------
25287 
25289 // no need for call logging as already failed
25290 {
25291 /*
25292  In order to reload as a session file:
25293 
25294  NB: Don't change it to a .txt file, as the '\0' characters will be changed to spaces if it is subsequently
25295  saved. USE NOTEPAD++ instead.
25296 
25297  strip out:-
25298 
25299  up to but excluding ***Interface***
25300  from & including ***ConstructPrefDir PrefDirVector***
25301  to but excluding ***PrefDirs***
25302  if there is a single line ***UserGraphics*** delete it (won't be present if no graphics)
25303  from & including ***ConstructRoute PrefDirVector***
25304  to but excluding ***Routes***
25305  from & including ***ChangingLCVector*** to but excluding ***Timetable*** [if have ***No timetable loaded*** then can't use as a session file]
25306  from & including ***No editing timetable*** or ***Editing timetable - [title]***
25307  to but excluding ***TimetableClock***
25308  from but excluding last entry of type 'End of file at vx.x.x'
25309  to end of file
25310 
25311  and save as a .ssn file.
25312 
25313  In order to load as a railway file: Note if error involves cut/copy etc with prefdirs then need to drop the check of CheckCount
25314 
25315  NB: Don't change it to a .txt file, as the '\0' characters will be changed to spaces if it is subsequently saved
25316 
25317  note or copy the version information at the top of the file
25318  copy the two numbers on rows 7 & 8 below ***Interface*** (i.e ***Interface*** = row 0) on their own lines immediately after the ***Track*** line
25319  (these become DisplayOffsetH & V)
25320  strip out up to but excluding ***Track*** - this is needed to keep the \0 entry at end of ***Track***
25321  add the version number either before or instead of ***Track***, ensuring that the \0 is retained
25322  the next line after the two insertions should contain the number of active elements.
25323  strip out ***Text*** including the \0
25324  strip out from & including ***ConstructPrefDir PrefDirVector*** to & including ***PrefDirs*** (and the \0's)
25325  strip out ***UserGraphics*** including the \0 <--this will be missing if none
25326  strip out from & including ***ConstructRoute PrefDirVector*** including the \0 to the end of the file
25327  the last entry should be '************NUL CR LF' (after all the pref dirs)
25328  rename as .dev or .rly file
25329 
25330  BUT - note that signals (and points, though they won't show) will be set as they were left. To reset to red, load a suitable timetable & select
25331  'Operate' then 'Exit operation'.
25332 */
25333 
25334 /*
25335  In order to extract a timetable:
25336 
25337  NB: Don't change it to a .txt file, as the '\0' characters will be changed to spaces if it is subsequently saved
25338 
25339  set wordwrap to window on
25340  strip out all to and including ***Timetable*** or ***Editing timetable.... depending which is to be saved
25341  ensure any text before start time ends with \0, otherwise don't need the \0
25342  strip out all after the final \0 immediately before ***End*** or ***End of timetable***, but ensure leave the final \0
25343  save as a .ttb file
25344 */
25345 
25346  Screen->Cursor = TCursor(-11); // Hourglass;
25347  AnsiString ErrorFileStr = CurDir + "\\errorlog.err";
25348  std::ofstream ErrorFile(ErrorFileStr.c_str());
25349 
25350  if(!(ErrorFile.fail()))
25351  {
25352 // save mouse position relative to mainscreen
25353  int ScreenX = Mouse->CursorPos.x - MainScreen->ClientOrigin.x;
25354  int ScreenY = Mouse->CursorPos.y - MainScreen->ClientOrigin.y;
25355  AnsiString MouseStr = "Posx: " + AnsiString(ScreenX) + "; Posy: " + AnsiString(ScreenY);
25356  Utilities->SaveFileString(ErrorFile, MouseStr);
25357 // save call stack
25358  Utilities->SaveFileString(ErrorFile, "***Call stack***");
25359  for(unsigned int x = 0; x < Utilities->CallLog.size(); x++)
25360  {
25361  AnsiString Item = Utilities->CallLog.at(x);
25362  ErrorFile << Item.c_str() << '\n';
25363  }
25364 // save event log
25365  Utilities->SaveFileString(ErrorFile, "***Event log***");
25366  for(unsigned int x = 0; x < Utilities->EventLog.size(); x++)
25367  {
25368  AnsiString Item = Utilities->EventLog.at(x);
25369  ErrorFile << Item.c_str() << '\n';
25370  }
25371 // save interface
25372  Utilities->SaveFileString(ErrorFile, "***Interface***");
25373  SaveInterface(1, ErrorFile);
25374 // save track elements
25375  Utilities->SaveFileString(ErrorFile, "***Track***");
25376  if(Track->UserGraphicVector.empty())
25377  {
25378  Track->SaveTrack(2, ErrorFile, false); // false for no graphics (**Active elements** saved as marker)
25379  }
25380  else
25381  {
25382  Track->SaveTrack(12, ErrorFile, true); // true for graphics to be saved (**Active elements**1 saved as marker)
25383  }
25384 // save text elements
25385  Utilities->SaveFileString(ErrorFile, "***Text***");
25386  TextHandler->SaveText(3, ErrorFile);
25387 // save ConstructPrefDir PrefDirVector elements
25388  Utilities->SaveFileString(ErrorFile, "***ConstructPrefDir PrefDirVector***");
25389  ConstructPrefDir->SavePrefDirVector(7, ErrorFile);
25390 // save ConstructPrefDir SearchVector elements
25391  Utilities->SaveFileString(ErrorFile, "***ConstructPrefDir SearchVector***");
25392  ConstructPrefDir->SaveSearchVector(0, ErrorFile);
25393 // save EveryPrefDir elements
25394  Utilities->SaveFileString(ErrorFile, "***PrefDirs***");
25395  EveryPrefDir->SavePrefDirVector(3, ErrorFile);
25396  if(!Track->UserGraphicVector.empty())
25397  {
25398  // save user graphics
25399  Utilities->SaveFileString(ErrorFile, "***UserGraphics***");
25400  Track->SaveUserGraphics(3, ErrorFile);
25401  }
25402 // save ConstructRoute PrefDirVector
25403  Utilities->SaveFileString(ErrorFile, "***ConstructRoute PrefDirVector***");
25404  ConstructRoute->SavePrefDirVector(4, ErrorFile);
25405 // save ConstructRoute SearchVector
25406  Utilities->SaveFileString(ErrorFile, "***ConstructRoute SearchVector***");
25407  ConstructRoute->SaveSearchVector(1, ErrorFile);
25408 // save AllRoutes
25409  Utilities->SaveFileString(ErrorFile, "***Routes***");
25410  AllRoutes->SaveRoutes(1, ErrorFile);
25411 // save LockedRoutes
25412  Utilities->SaveFileString(ErrorFile, "***Locked routes***");
25414 // save ContinuationAutoSigEntries
25415  Utilities->SaveFileString(ErrorFile, "***ContinuationAutoSigEntries***");
25417 // save BarriersDownVector
25418  Utilities->SaveFileString(ErrorFile, "***BarriersDownVector***");
25419  Track->SaveSessionBarriersDownVector(1, ErrorFile);
25420 // save ChangingLCVector
25421  Utilities->SaveFileString(ErrorFile, "***ChangingLCVector***");
25422  Track->SaveChangingLCVector(0, ErrorFile);
25423 // save loaded timetable
25424  if(TimetableTitle == "")
25425  {
25426  Utilities->SaveFileString(ErrorFile, "***No timetable loaded***");
25427  }
25428  else
25429  {
25430  Utilities->SaveFileString(ErrorFile, "***Timetable***");
25431  if(!(SaveTimetableToSessionFile(1, ErrorFile, ErrorFileStr)))
25432  {
25433  Utilities->SaveFileString(ErrorFile, "***Loaded timetable failed to save***");
25434  }
25435  }
25436 // save editing timetable
25437  if(CreateEditTTTitle == "")
25438  {
25439  Utilities->SaveFileString(ErrorFile, "***No editing timetable***");
25440  }
25441  else
25442  {
25443  Utilities->SaveFileString(ErrorFile, "***Editing timetable - " + CreateEditTTTitle + "***");
25444  if(!(SaveTimetableToErrorFile(1, ErrorFile, ErrorFileStr, CreateEditTTFileName)))
25445  {
25446  Utilities->SaveFileString(ErrorFile, "***Editing timetable failed to save***");
25447  }
25448  }
25449 // save TimetableClock
25450  Utilities->SaveFileString(ErrorFile, "***TimetableClock***");
25451  Utilities->SaveFileDouble(ErrorFile, double(TrainController->TTClockTime));
25452 // save trains
25453  Utilities->SaveFileString(ErrorFile, "***Trains***");
25454  TrainController->SaveSessionTrains(1, ErrorFile);
25455 // save performance file
25456  Utilities->SaveFileString(ErrorFile, "***Performance file***");
25457  SavePerformanceFile(1, ErrorFile);
25458  Utilities->SaveFileString(ErrorFile, "***End of performance file***");
25459 // addition at v2.4.1 to save TrainController->AvHoursIntValue + any future additions
25460  Utilities->SaveFileString(ErrorFile, "***Additions after v2.3.1***");
25463  Utilities->SaveFileString(ErrorFile, "***Failed Trains***");
25464  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
25465  {
25467  {
25470  }
25471  }
25472  Utilities->SaveFileInt(ErrorFile, -1); // marker for end of failed trains
25473  Utilities->SaveFileString(ErrorFile, "End of file at v2.4.1");
25474 // end of v2.4.1 addition
25475 
25476 // added at v2.7.0 - extras saved later in session file (added at v2.9.2)
25478  Utilities->SaveFileString(ErrorFile, "End of file at v2.7.0");
25479 // end of v2.7.0 addition
25480 
25481 // added at v2.9.1
25487  Utilities->SaveFileString(ErrorFile, "End of v2.9.1 additions"); //changed from '2.9.0' at v2.9.2
25488 // end of v2.9.1 additions
25489 
25490 //added at v2.11.0
25491 //add SkippedTTEvents
25493 // add data for trains in process of skipping timetable events (i.e. those with events after a future departure)
25494  if(!TrainController->TrainVector.empty())
25495  {
25496  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
25497  {
25498  TTrain Train = TrainController->TrainVectorAt(83, x);
25499  if(Train.SkippedDeparture)
25500  {
25501  Utilities->SaveFileInt(ErrorFile, Train.TrainID);
25502  Utilities->SaveFileBool(ErrorFile, Train.SkippedDeparture);
25503  Utilities->SaveFileBool(ErrorFile, Train.ActionsSkippedFlag);
25504  Utilities->SaveFileInt(ErrorFile, Train.SkipPtrValue);
25505  Utilities->SaveFileInt(ErrorFile, Train.TrainSkippedEvents);
25506  }
25507  }
25508  }
25509  Utilities->SaveFileString(ErrorFile, "End of file at v2.11.0");
25510 //end of 2.11.0 additions
25511 
25512 //additions at v2.12.0 - become new service early & treat pass as departure
25513  if(!TrainController->TrainVector.empty())
25514  {
25515  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
25516  {
25517  TTrain Train = TrainController->TrainVectorAt(86, x);
25518  if(Train.TreatPassAsTimeLocDeparture) //this can only apply once for a single train (service repeats are separate trains)
25519  {
25520  Utilities->SaveFileInt(ErrorFile, Train.TrainID);
25521  }
25522  }
25523  }
25524  Utilities->SaveFileString(ErrorFile, "End of file at v2.12.0");
25525 //end of v2.12.0 additions
25526 
25527 //additions at v2.13.0 - random delays
25528 //No need to save Utilities->LastDelayTTClockTime - makes little difference and would cause corruption in any v2.13.0 Beta sessions
25529  Utilities->SaveFileInt(ErrorFile, Utilities->CumulativeDelayedRandMinsAllTrains); //to allow for exited and removed trains
25530  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
25531  { //if empty will skip
25532  TTrain Train = TrainController->TrainVectorAt(90, x);
25533  Utilities->SaveFileDouble(ErrorFile, Train.NewDelay);
25534  Utilities->SaveFileDouble(ErrorFile, Train.DelayedRandMins);
25536  Utilities->SaveFileDouble(ErrorFile, double(Train.ActualArrivalTime));
25537  //ReleaseTime already loaded
25538  }
25539  //save failed point info
25540  Utilities->SaveFileInt(ErrorFile, Track->FailedPointsVector.size()); //number of failed points
25541  for(unsigned int x = 0; x < Track->FailedPointsVector.size(); x++)
25542  { //if empty will skip, when reload set Failed to true & SpeedLimits to 10km/h
25543  TTrackElement &TE = Track->TrackElementAt(1551, Track->FailedPointsVector.at(x).TVPos);
25544  Utilities->SaveFileInt(ErrorFile, Track->FailedPointsVector.at(x).TVPos);
25547  Utilities->SaveFileDouble(ErrorFile, double(Track->FailedPointsVector.at(x).FailureTime));
25548  Utilities->SaveFileDouble(ErrorFile, double(Track->FailedPointsVector.at(x).RepairTime));
25549  }
25550  //save failed signal info
25551  Utilities->SaveFileInt(ErrorFile, Track->FailedSignalsVector.size()); //number of failed signals
25552  for(unsigned int x = 0; x < Track->FailedSignalsVector.size(); x++)
25553  { //if empty will skip, when reload set Failed to true
25554  Utilities->SaveFileInt(ErrorFile, Track->FailedSignalsVector.at(x).TVPos);
25555  Utilities->SaveFileDouble(ErrorFile, double(Track->FailedSignalsVector.at(x).FailureTime));
25556  Utilities->SaveFileDouble(ErrorFile, double(Track->FailedSignalsVector.at(x).RepairTime));
25557  }
25558  //save TSR info
25559  Utilities->SaveFileInt(ErrorFile, Track->TSRVector.size()); //number of TSRs
25560  for(unsigned int x = 0; x < Track->TSRVector.size(); x++)
25561  { //if empty will skip, when reload set Failed to true & SpeedLimit to 10km/h
25562  TTrackElement &TE = Track->TrackElementAt(1552, Track->TSRVector.at(x).TVPos);
25563  Utilities->SaveFileInt(ErrorFile, Track->TSRVector.at(x).TVPos);
25565  Utilities->SaveFileDouble(ErrorFile, double(Track->TSRVector.at(x).FailureTime));
25566  Utilities->SaveFileDouble(ErrorFile, double(Track->TSRVector.at(x).RepairTime));
25567  }
25568  Utilities->SaveFileString(ErrorFile, "End of file at v2.13.0");
25569 //end of v2.13.0 additions
25570 //additions at v2.14.0 - delays and failures
25571  if(Utilities->DelayMode == Minor)
25572  {
25573  Utilities->SaveFileInt(ErrorFile, 1);
25574  }
25575  else if(Utilities->DelayMode == Moderate)
25576  {
25577  Utilities->SaveFileInt(ErrorFile, 2);
25578  }
25579  else if(Utilities->DelayMode == Major)
25580  {
25581  Utilities->SaveFileInt(ErrorFile, 3);
25582  }
25583  else
25584  {
25585  Utilities->SaveFileInt(ErrorFile, 0);
25586  }
25587  if(Utilities->FailureMode == FMinor)
25588  {
25589  Utilities->SaveFileInt(ErrorFile, 1);
25590  }
25591  else if(Utilities->FailureMode == FModerate)
25592  {
25593  Utilities->SaveFileInt(ErrorFile, 2);
25594  }
25595  else if(Utilities->FailureMode == FMajor)
25596  {
25597  Utilities->SaveFileInt(ErrorFile, 3);
25598  }
25599  else
25600  {
25601  Utilities->SaveFileInt(ErrorFile, 0);
25602  }
25603  Utilities->SaveFileString(ErrorFile, "End of file at v2.14.0");
25604 //end of v2.14.0 additions
25605 //additions at v2.16.1 - train descriptions
25606  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
25607  {
25608  Utilities->SaveFileString(ErrorFile, TVIt->Description);
25609  }
25610  Utilities->SaveFileString(ErrorFile, "End of file at v2.16.1");
25611 //end of v2.16.1 additions
25612 //additions at v2.19.0 - reminders save AnsiString of trainvector position + '-' + action vector position of exch reminder
25613  AnsiString ReminderEntry = "";
25614  if(!TrainController->TrainVector.empty())
25615  {
25616  for(unsigned int x = 0; x < TrainController->TrainVector.size(); x++)
25617  {
25618  TTrain Train = TrainController->TrainVectorAt(104, x);
25620  for(unsigned int y = 0; y < AV.size(); y++)
25621  {
25622  if(AV.at(y).Reminder > 0)
25623  {
25624  ReminderEntry = AnsiString(x + (10000 * AV.at(y).Reminder)) + '-' + AnsiString(y); //allows up to 9999 trains
25625  Utilities->SaveFileString(ErrorFile, ReminderEntry);
25626  }
25627  }
25628  }
25629  }
25630  Utilities->SaveFileString(ErrorFile, "End of file at v2.19.0");
25631 //end of v2.19.0 additions
25632 
25633 //additions at v2.20.2 - AllowedToPassRedSignal flag for each train - without this could save a session after permission given but when reloaded would give a SPAD
25634  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
25635  {
25636  Utilities->SaveFileBool(ErrorFile, TVIt->AllowedToPassRedSignal);
25637  }
25638  Utilities->SaveFileString(ErrorFile, "End of file at v2.20.2");
25639 //end of v2.20.2 additions
25640 //additions at v2.20.3 - remaining train values
25641 
25642  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
25643  {
25644  TTrain TempTrain = *TVIt; //for diagnostics as debugger can't handle iterators
25645  Utilities->SaveFileBool(ErrorFile, TVIt->RemainHereLogNotSent);
25646  Utilities->SaveFileString(ErrorFile, TVIt->HeadCode);
25647  Utilities->SaveFileBool(ErrorFile, TVIt->FinishJoinLogSent);
25648  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoFrontSplitMessage);
25649  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoRearSplitMessage);
25650  Utilities->SaveFileBool(ErrorFile, TVIt->FailedTrainNoFinishJoinMessage);
25651  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoJoinedByMessage);
25652  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoCDTMessage);
25653  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoNewServiceMessage);
25654  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoNewShuttleFromNonRepeatMessage);
25655  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoRepeatShuttleMessage);
25656  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerNoRepeatShuttleOrNewServiceMessage);
25657  Utilities->SaveFileBool(ErrorFile, TVIt->ZeroPowerDepartMessage);
25658  Utilities->SaveFileBool(ErrorFile, TVIt->TrainInFrontMessage);
25659  Utilities->SaveFileBool(ErrorFile, TVIt->TrainFailurePending);
25660  Utilities->SaveFileBool(ErrorFile, TVIt->BufferZoomOutFlashRequired);
25661  Utilities->SaveFileBool(ErrorFile, TVIt->JoinedOtherTrainFlag);
25662  Utilities->SaveFileBool(ErrorFile, TVIt->LastSigPassedFailed);
25663  Utilities->SaveFileBool(ErrorFile, TVIt->LeavingUnderSigControlAtContinuation);
25664  Utilities->SaveFileBool(ErrorFile, TVIt->SignallerStoppingFlag);
25665  Utilities->SaveFileBool(ErrorFile, TVIt->StationStopCalculated);
25666  Utilities->SaveFileBool(ErrorFile, TVIt->StepForwardFlag);
25667  Utilities->SaveFileDouble(ErrorFile, TVIt->SignallerStopBrakeRate);
25668  Utilities->SaveFileDouble(ErrorFile, TVIt->MinsDelayed);
25669  Utilities->SaveFileDouble(ErrorFile, TVIt->OpTimeToAct);
25670  Utilities->SaveFileDouble(ErrorFile, TVIt->TimeToExit);
25671  Utilities->SaveFileDouble(ErrorFile, TVIt->FirstLaterStopRecoverableTime);
25672  Utilities->SaveFileInt(ErrorFile, TVIt->DistanceToStationStop);
25673  Utilities->SaveFileInt(ErrorFile, TVIt->UpdateCounter);
25674  Utilities->SaveFileInt(ErrorFile, TVIt->ExitPair.first);
25675  Utilities->SaveFileInt(ErrorFile, TVIt->ExitPair.second);
25676  Utilities->SaveFileBool(ErrorFile, TVIt->StoppedWithoutPower);
25677  Utilities->SaveFileBool(ErrorFile, TVIt->TrainInFront);
25678  Utilities->SaveFileInt(ErrorFile, TVIt->OldZoomOutElement[0]);
25679  Utilities->SaveFileInt(ErrorFile, TVIt->OldZoomOutElement[1]);
25680  Utilities->SaveFileInt(ErrorFile, TVIt->OldZoomOutElement[2]);
25681  Utilities->SaveFileString(ErrorFile, TVIt->SelReminderString);
25682  Utilities->SaveFileString(ErrorFile, TVIt->SelSkipString);
25683  Utilities->SaveFileBool(ErrorFile, TVIt->AllowedToPassRedSignal); //corrected earlier error in v2.20.2 LoadSession, see above
25684  Utilities->SaveFileString(ErrorFile, "****"); //end of train marker
25685  }
25686  Utilities->SaveFileString(ErrorFile, "End of file at v2.20.3");
25687 //end of v2.20.3 additions
25688 //additions at v2.23.0
25689  for(TTrainController::TTrainVector::iterator TVIt = TrainController->TrainVector.begin(); TVIt != TrainController->TrainVector.end(); TVIt++)
25690  {
25691  Utilities->SaveFileBool(ErrorFile, TVIt->NonDefaultMinDwellTimeFlag);
25692  Utilities->SaveFileDouble(ErrorFile, TVIt->ArrivalMinDwellTime); //may be the default value of 30secs
25693  }
25694  Utilities->SaveFileString(ErrorFile, "End of file at v2.23.0");
25695 
25696 //REMAINDER STAY AT END OF FILE
25697 // addition at v2.8.0 in case of clipboard
25698 // errors <-- keep at end of file as not wanted in a reconstructed session file
25699  Utilities->SaveFileInt(ErrorFile, SelectBitmap->Height); // extras for new clipboard functions
25700  Utilities->SaveFileInt(ErrorFile, SelectBitmap->Width);
25701  Utilities->SaveFileInt(ErrorFile, SelectBitmapHLoc); // paste location
25702  Utilities->SaveFileInt(ErrorFile, SelectBitmapVLoc);
25703  Utilities->SaveFileInt(ErrorFile, SelectRect.left); // original selection location
25704  Utilities->SaveFileInt(ErrorFile, SelectRect.top);
25705  Utilities->SaveFileInt(ErrorFile, SelectRect.right);
25706  Utilities->SaveFileInt(ErrorFile, SelectRect.bottom);
25707  Utilities->SaveFileString(ErrorFile, "End of clipboard additions");
25708 // end of 2.8.0 addition
25709  ErrorFile.close();
25710  }
25711  else
25712  {
25713  TrainController->StopTTClockMessage(6, "Error file failed to open, error log won't be saved.");
25714  }
25715  Screen->Cursor = TCursor(-2); // Arrow
25716 }
25717 
25718 // ---------------------------------------------------------------------------
25719 
25720 void TInterface::SaveTempTimetableFile(int Caller, AnsiString InFileName)
25721 // the .ttb section is delimited by '\n' followed by "***End***"
25722 // first create a .ttb file in the working folder exactly like the original
25723 
25724 // Note: this type of file use failed when used to resave timetable.tmp from temp .ttb file, but changed that to avoid so many rapid
25725 // file actions in quick succession & been OK since then, but nevertheless keep the 10 retries before giving message to be on safe side
25726 {
25727  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTempTimetableFile");
25728  if((TempTTFileName != "") && FileExists(TempTTFileName))
25729  {
25730  DeleteFile(TempTTFileName);
25731  }
25732  int TempTTFileNumber = 0;
25733 
25734  while(FileExists(CurDir + "\\TmpTT" + AnsiString(TempTTFileNumber) + ".tmp"))
25735  {
25736  TempTTFileNumber++;
25737  }
25738  TempTTFileName = CurDir + "\\TmpTT" + AnsiString(TempTTFileNumber) + ".tmp";
25739  int InHandle = FileOpen(InFileName, fmOpenRead);
25740  int Count = 0;
25741 
25742  while(InHandle < 0) // sometimes fails, have 10 retries before giving message
25743  {
25744  InHandle = FileOpen(InFileName, fmOpenRead);
25745  Count++;
25746  Delay(2, 50); // 50mSec delay between tries
25747  if(Count > 10)
25748  {
25749  ShowMessage("Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
25750  Utilities->CallLogPop(1400);
25751  return;
25752  }
25753  }
25754  int OutHandle = FileCreate(TempTTFileName);
25755 
25756  Count = 0;
25757  while(OutHandle < 0) // sometimes fails, have 10 retries before giving message
25758  {
25759  OutHandle = FileCreate(TempTTFileName);
25760  Count++;
25761  Delay(3, 50); // 50mSec delay between tries
25762  if(Count > 10)
25763  {
25764  ShowMessage("Failed to save temporary timetable file, sessions can't be saved - try again, may only be a temporary problem");
25765  FileClose(InHandle);
25766  Utilities->CallLogPop(1401);
25767  return;
25768  }
25769  }
25770  int CountIn, CountOut;
25771  char *Buffer = new char[10000]; // can't use LoadFileString as that expects a '\0' delimiter
25772 
25773  while(true)
25774  {
25775  CountIn = FileRead(InHandle, Buffer, 10000);
25776  CountOut = FileWrite(OutHandle, Buffer, CountIn);
25777  if(CountOut != CountIn)
25778  {
25779  ShowMessage("Error in writing to the temporary timetable file, sessions can't be saved - try again, may only be a temporary problem");
25780  delete[]Buffer;
25781  FileClose(InHandle);
25782  FileClose(OutHandle);
25783  Utilities->CallLogPop(1402);
25784  return;
25785  }
25786  if(CountIn < 10000)
25787  {
25788  break;
25789  }
25790  }
25791  delete[]Buffer;
25792  FileClose(InHandle);
25793  FileClose(OutHandle);
25794  Utilities->CallLogPop(1403);
25795 }
25796 
25797 // ---------------------------------------------------------------------------
25798 
25799 void TInterface::SetTrackLengths(int Caller, int Distance, int SpeedLimit) // Distance & SpeedLimit are -1 for no change to that parameter
25800 /*
25801  Rules: Platforms are fixed length elements of 100m and aren't changed - no, see note below. Variable length elements can't be less than 10m.
25802  above changed in v2.4.0 to be variable as other track, but if <50m or >200m a warning is given
25803 
25804  Enter with DistanceVector containing the PrefDir to be set, Distance containing the required sum of all element lengths,
25805  and SpeedLimit containing the speed limit. If either of these is -1 (can be -1 separately) then no change is to be made to it.
25806  Return for an empty DistanceVector. Deal first with a single element in the vector, giving a message if there is a platform there.
25807  Now set exit link position (XLinkPos) value for the first element in DistanceVector by checking which link connects to the second
25808  element in the vector, and give a warning message if fail to find it. Now have to make two passes through the vector, firstly to
25809  sum the fixed lengths, count the number of variable length elements and set the speed limit, and secondly to set the lengths.
25810  Firstly store the first XLinkPos so don't have to recalculate it for the second pass. On the first pass examine each element,
25811  incrementing the variable element count or summing the fixed length count as go along, and setting the speed limits providing
25812  SpeedLimit isn't -1. If Distance was -1 then still go through but don't count anything, just set the speed limits. Recalculate
25813  the next XLinkPos for each succeeding element.
25814  After the first pass return if Distance was -1 as in that case have now finished. Otherwise check if the distance to be set is less than
25815  the minimum possible within the rules, and if so give a message and return. Also give a warning message if there aren't any variable length
25816  elements. Now enter the second pass. In this the length of each variable element is set to int(RemainingDistance/RemainingVarElements) and
25817  fixed length elements are ignored. After each variable length element is set the RemainingDistance and RemainingVarElements are recalculated
25818  ready for the next setting. In this way there is never more than 1 difference between any two variable length elements and the total
25819  distance sums exactly to the value required. A check is made after every variable length element has been set to see whether RemainingDistance
25820  and RemainingVarElements are zero, and if they don't reach zero together (which they should after the last variable length element has
25821  been set), an error message is given.
25822 */
25823 
25824 {
25825  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetLengths," + AnsiString(Distance) + "," + AnsiString(SpeedLimit));
25826  bool FoundFlag;
25827 
25828 // ResetDistanceElements(4);
25829  if(ConstructPrefDir->PrefDirSize() == 0)
25830  {
25831  Utilities->CallLogPop(608);
25832  return;
25833  }
25834 // must have PrefDir size of at least 2
25835 
25836 // first pass to count number of variable length elements, sum fixed lengths & set speed limit
25837 // for version in v2.4.0 have no fixed length elements but leave code as is as much as possible
25838  int VarElements = 0; // FixedLength = 0; drop this in v2.4.0
25839  bool NamedLocPresent = false;
25840 
25841  for(unsigned int x = 0; x < ConstructPrefDir->PrefDirSize(); x++)
25842  {
25843  TPrefDirElement PrefDirElement = ConstructPrefDir->GetFixedPrefDirElementAt(167, x);
25844  TTrackElement & TE = Track->TrackElementAt(34, Track->GetVectorPositionFromTrackMap(28, PrefDirElement.HLoc, PrefDirElement.VLoc, FoundFlag));
25845  if(!FoundFlag)
25846  {
25847  throw Exception("Error - failed to find track element at " + AnsiString(TE.HLoc) + " & " + AnsiString(TE.VLoc) + " in SetLengths");
25848  }
25849  if((Distance != -1) && (!Track->IsPlatformOrNamedNonStationLocationPresent(2, TE.HLoc, TE.VLoc)))
25850  {
25851  VarElements++;
25852  }
25853  else if((Distance != -1) && (Track->IsPlatformOrNamedNonStationLocationPresent(3, TE.HLoc, TE.VLoc)))
25854  {
25855  VarElements++; // added in v2.4.0 for no fixed elements
25856  NamedLocPresent = true;
25857 // FixedLength+= DefaultTrackLength; dropped in v2.4.0 for no fixed elements
25858  }
25859  if((PrefDirElement.GetELinkPos() < 2) && (PrefDirElement.GetXLinkPos() < 2)) // could be points
25860  {
25861  if(SpeedLimit != -1)
25862  {
25863  TE.SpeedLimit01 = SpeedLimit;
25864  }
25865  }
25866  else
25867  {
25868  if(SpeedLimit != -1)
25869  {
25870  TE.SpeedLimit23 = SpeedLimit;
25871  }
25872  }
25873  }
25874  if(Distance == -1) // can't return before this as need to set speed limits
25875  {
25876  Utilities->CallLogPop(612);
25877  return;
25878  }
25879  if((NamedLocPresent) && (VarElements > 0) && ((Distance / VarElements) < 50)) // these two additions are for in v2.4.0
25880  {
25881  if(!TooShortMessageSentFlag) //added at v2.9.1
25882  {
25883  ShowMessage("Note: Named location elements are quite short. If they are too short the simulation might be too unrealistic.\n\nThis message will not be shown again.");
25884  TooShortMessageSentFlag = true; //added at v2.9.1
25885  }
25886  }
25887  if((NamedLocPresent) && (VarElements > 0) && ((Distance / VarElements) > 200))
25888  {
25889  if(!TooLongMessageSentFlag) //added at v2.9.1
25890  {
25891  ShowMessage("Note: Named location elements are quite long. If they are too long the simulation might be too unrealistic.\n\nThis message will not be shown again.");
25892  TooLongMessageSentFlag = true; //added at v2.9.1
25893  }
25894  }
25895  if((VarElements * 10) > Distance) // removed '+ FixedLength'
25896  {
25897  ShowMessage("Required distance is less than the minimum, will set each element to the minimum (10m)");
25898  Distance = (VarElements * 10); // removed '+ FixedLength'
25899  }
25900  if(VarElements == 0)
25901  {
25902 // ShowMessage("Unable to set distance as all elements are of fixed length"); as was
25903  ShowMessage("No elements selected"); // probably don't need this but include for safety
25904  Utilities->CallLogPop(613);
25905  return;
25906  }
25907 // second pass, set variable lengths
25908  int RemainingDistance = Distance, RemainingVarElements = VarElements, NextLength = RemainingDistance / VarElements; // removed ' - FixedLength'
25909 
25910  for(unsigned int x = 0; x < ConstructPrefDir->PrefDirSize(); x++)
25911  {
25912  TPrefDirElement PrefDirElement = ConstructPrefDir->GetFixedPrefDirElementAt(168, x);
25913  TTrackElement & TE = Track->TrackElementAt(35, Track->GetVectorPositionFromTrackMap(29, PrefDirElement.HLoc, PrefDirElement.VLoc, FoundFlag));
25914 // if(!Track->IsPlatformOrNamedNonStationLocationPresent(4, TE.HLoc, TE.VLoc)) //variable lengths dropped in v2.4.0
25915 // {
25916  if(NextLength < 10)
25917  {
25918  NextLength = 10; // added for safety
25919  }
25920  if(TE.TrackType == Points)
25921  {
25922  if((PrefDirElement.GetELinkPos() == 1) || (PrefDirElement.GetXLinkPos() == 1))
25923  {
25924  TE.Length01 = NextLength;
25925  }
25926  else
25927  {
25928  TE.Length23 = NextLength;
25929  }
25930  }
25931  else
25932  {
25933  if(PrefDirElement.GetELinkPos() < 2)
25934  {
25935  TE.Length01 = NextLength;
25936  }
25937  else
25938  {
25939  TE.Length23 = NextLength;
25940  }
25941  }
25942  RemainingDistance -= NextLength;
25943  RemainingVarElements--;
25944  if(RemainingVarElements > 0)
25945  {
25946  NextLength = RemainingDistance / RemainingVarElements;
25947  }
25948  else
25949  {
25950  NextLength = 10;
25951  }
25952 /* removed these as using integer division & that sometimes problematic. None of these errors ever reported but be safe
25953  if((RemainingDistance == 0) && (RemainingVarElements != 0))
25954  {
25955  throw Exception("Error RemainingDistance == 0 & RemainingVarElements != 0");
25956  }
25957  if((RemainingDistance != 0) && (RemainingVarElements == 0))
25958  {
25959  throw Exception("Error RemainingDistance != 0 & RemainingVarElements == 0");
25960  }
25961 */
25962 // }
25963  }
25964  Utilities->CallLogPop(614);
25965 }
25966 
25967 // ---------------------------------------------------------------------------
25968 
25970 {
25971  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveAsSubroutine");
25973  {
25974  ShowMessage("Nothing to save!");
25975  }
25976  else
25977  {
25978  if(Track->IsReadyForOperation(false))
25979  {
25980  SaveRailwayDialog->Filter = "Development file (*.dev)|*.dev|Railway file (*.rly)|*.rly";
25981  }
25982  else
25983  {
25984  SaveRailwayDialog->Filter = "Development file (*.dev)|*.dev";
25985  }
25986  if(SaveRailwayDialog->Execute())
25987  {
25988  if(SaveRailwayDialog->InitialDir != TPath::GetDirectoryName(SaveRailwayDialog->FileName)) // new at v2.6.0 to retain a new directory
25989  {
25990  SaveRailwayDialog->InitialDir = TPath::GetDirectoryName(SaveRailwayDialog->FileName);
25991  LoadRailwayDialog->InitialDir = TPath::GetDirectoryName(SaveRailwayDialog->FileName);
25992  }
25993  Screen->Cursor = TCursor(-11); // Hourglass;
25994  TrainController->LogEvent("Save " + SaveRailwayDialog->FileName);
25995  AnsiString Extension = "";
25996  if(SaveRailwayDialog->FileName.Length() > 2)
25997  {
25998  Extension = AnsiString(SaveRailwayDialog->FileName).SubString(AnsiString(SaveRailwayDialog->FileName).Length() - 2, 3).UpperCase();
25999  }
26000  if((Extension == "DEV") || (Track->IsReadyForOperation(true) && (Extension == "RLY"))) // give duplicated location name message if appropriate
26001  {
26002  std::ofstream VecFile(AnsiString(SaveRailwayDialog->FileName).c_str());
26003  if(!(VecFile.fail()))
26004  {
26008  // save track elements
26009  if(Track->UserGraphicVector.empty())
26010  {
26011  Track->SaveTrack(1, VecFile, false); // false for no graphics (**Active elements** saved as marker)
26012  }
26013  else
26014  {
26015  Track->SaveTrack(13, VecFile, true); // true for graphics to be saved (**Active elements**1 saved as marker)
26016  }
26017  // save text elements
26018  TextHandler->SaveText(1, VecFile);
26019  // save PrefDir elements
26020  EveryPrefDir->SavePrefDirVector(1, VecFile);
26021  if(!Track->UserGraphicVector.empty())
26022  {
26023  // save user graphics
26024  Track->SaveUserGraphics(4, VecFile);
26025  }
26026  VecFile.close();
26027  SavedFileName = SaveRailwayDialog->FileName; // includes the full PrefDir
26028  if(SavedFileName != "") // shouldn't be "" at this stage but leave in as a safeguard
26029  {
26030  char LastChar = SavedFileName[SavedFileName.Length()];
26031  if((LastChar == 'y') || (LastChar == 'Y'))
26032  {
26033  RlyFile = true;
26034  }
26035  else
26036  {
26037  RlyFile = false;
26038  }
26039  }
26040  else
26041  {
26042  RlyFile = false;
26043  }
26044  FileChangedFlag = false;
26045  for(int x = SaveRailwayDialog->FileName.Length(); x > 0; x--)
26046  {
26047  if(SaveRailwayDialog->FileName[x] == '\\')
26048  {
26049  RailwayTitle = SaveRailwayDialog->FileName.SubString(x + 1, SaveRailwayDialog->FileName.Length() - x - 4);
26050  // TimetableTitle = ""; leave this as is, no need to unload a tt just because saved railway
26051  SetCaption(7);
26052  break;
26053  }
26054  }
26055  Level1Mode = BaseMode;
26056  SetLevel1Mode(13); // to disable the save option
26057  } // if(!(VecFile.fail()))
26058  else
26059  {
26060  ShowMessage("File open failed prior to save");
26061  }
26062  } // else following if(!Track->IsReadyForOperation() && (Extension != "DEV"))
26063  else
26064  {
26065  ShowMessage("Can't save: extension must be either '.dev', or '.rly' with railway ready for operation");
26066  }
26067  Screen->Cursor = TCursor(-2); // Arrow
26068  } // if(SaveRailwayDialog->Execute())
26069 
26070  }
26071  session_api_->dump(); // update session INI file //added at v2.10.0
26072  Utilities->CallLogPop(1546);
26073 }
26074 
26075 // ---------------------------------------------------------------------------
26076 
26078 {
26079  try
26080  {
26081  // no need for log as only setting values
26082  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrackModeEditMenu");
26083  CutMenuItem->Visible = true;
26084  CopyMenuItem->Visible = true;
26085  FlipMenuItem->Visible = true;
26086  MirrorMenuItem->Visible = true;
26087  RotRightMenuItem->Visible = true;
26088  RotLeftMenuItem->Visible = true;
26089  RotateMenuItem->Visible = true;
26090  PasteMenuItem->Visible = true;
26091  DeleteMenuItem->Visible = true;
26092  SelectLengthsMenuItem->Visible = true;
26093  ReselectMenuItem->Visible = true;
26094  // Application->MessageBox(L"Running SetTrackModeEditMenu", L"Message", MB_OK); //debug check
26095  CutMenuItem->Enabled = false;
26096  CopyMenuItem->Enabled = false;
26097  FlipMenuItem->Enabled = false;
26098  MirrorMenuItem->Enabled = false;
26099  RotRightMenuItem->Enabled = false;
26100  RotLeftMenuItem->Enabled = false;
26101  RotateMenuItem->Enabled = false;
26102  PasteMenuItem->Enabled = false;
26103  EditMenu->Enabled = false;
26104  System::WideChar ValidityBuffer[14];
26105  Clipboard()->GetTextBuf(ValidityBuffer, 14);
26106  ClpBrdValid = AnsiString(ValidityBuffer);
26107  Clipboard()->Close();
26108 
26109  // new section for 2.8.0 in case have valid clipboard from another application
26110  if(!SelectionValid)
26111  {
26112  if(ClpBrdValid == "RlyClpBrdCopy")
26113  {
26114  PasteMenuItem->Enabled = true;
26115  if(Level1Mode == TrackMode)
26116  {
26117  EditMenu->Enabled = true;
26118  }
26119  CopySelected = true;
26120  Track->CopyFlag = true;
26121  // Level1Mode = TrackMode;
26122  }
26123  else if(ClpBrdValid == "RlyClpBrd_Cut")
26124  {
26125  PasteMenuItem->Enabled = true;
26126  if(Level1Mode == TrackMode)
26127  {
26128  EditMenu->Enabled = true;
26129  }
26130  CopySelected = false;
26131  Track->CopyFlag = false;
26132  // Level1Mode = TrackMode;
26133  }
26134  }
26135  // end of new section
26136 
26137  // PasteWithAttributesMenuItem->Enabled = false; //new at v2.2.0 (dropped at 2.4.0 as all pastes are with attributes)
26138  ClpBrdValid = "";
26139  DeleteMenuItem->Enabled = false;
26140  SelectLengthsMenuItem->Enabled = false;
26141  if(SelectionValid)
26142  {
26143  ReselectMenuItem->Enabled = true;
26144  }
26145  else
26146  {
26147  ReselectMenuItem->Enabled = false;
26148  }
26149  SelectBiDirPrefDirsMenuItem->Visible = false;
26150  CheckPrefDirConflictsMenuItem->Visible = false;
26151  CancelSelectionMenuItem->Enabled = true;
26152  SelectMenuItem->Enabled = true;
26153 
26154  if(NoRailway() && (TrackElementPanel->Visible == false)) //added latter condition at v2.10.0 so can use select to add track elements in bulk
26155  {
26156  SelectMenuItem->Enabled = false;
26157  }
26158  else if(Level1Mode == TrackMode)
26159  {
26160  EditMenu->Enabled = true;
26161  }
26162  Utilities->CallLogPop(2273);
26163  }
26164  catch(const EClipboardException &e) //non-error catch - added at v2.10.0 after SamWainwright access denial error (08/09/21)
26165  //also reported by Bengt on 03/10/21
26166  {
26167  TrainController->LogEvent("EClipboardException in SetTrackModeEditMenu - message = " + e.Message);
26168  Utilities->CallLogPop(2321);
26169  }
26170 }
26171 
26172 // ---------------------------------------------------------------------------
26173 
26175 {
26176  // no need for caller or log as only setting values
26177  EditMenu->Enabled = true;
26178 
26179  CutMenuItem->Visible = false;
26180  CopyMenuItem->Visible = false;
26181  FlipMenuItem->Visible = false;
26182  MirrorMenuItem->Visible = false;
26183  RotRightMenuItem->Visible = false;
26184  RotLeftMenuItem->Visible = false;
26185  RotateMenuItem->Visible = false;
26186  PasteMenuItem->Visible = false;
26187 // PasteWithAttributesMenuItem->Visible = false; //added at v2.2.0 (dropped at 2.4.0 as all pastes are with attributes)
26188  DeleteMenuItem->Visible = false;
26189  SelectLengthsMenuItem->Visible = false;
26190  ReselectMenuItem->Visible = false;
26191 
26192  SelectBiDirPrefDirsMenuItem->Visible = true;
26193  CheckPrefDirConflictsMenuItem->Visible = true;
26194  SelectBiDirPrefDirsMenuItem->Enabled = false;
26195  CheckPrefDirConflictsMenuItem->Enabled = true;
26196  CancelSelectionMenuItem->Enabled = true;
26197  SelectMenuItem->Enabled = true;
26198 }
26199 
26200 // ---------------------------------------------------------------------------
26201 
26203 {
26204  return ((Track->NoActiveOrInactiveTrack(5)) && (TextHandler->TextVectorSize(8) == 0) && Track->UserGraphicVector.empty());
26205 }
26206 
26207 // ---------------------------------------------------------------------------
26208 
26210 {
26211  SelectRect.left = 0;
26212  SelectRect.right = 0;
26213  SelectRect.top = 0;
26214  SelectRect.bottom = 0;
26215 }
26216 
26217 // ---------------------------------------------------------------------------
26218 
26219 bool TInterface::EraseLocationNameText(int Caller, AnsiString Name, int &HPos, int &VPos)
26220 {
26221  // return position of erased name in HPos & VPos, return true for found & erased
26222  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EraseLocationNameText," + Name);
26223  bool TextFound = false;
26224 
26225 // if(Track->LocationNameMultiMap.find(Name) == Track->LocationNameMultiMap.end()) {} //name not in LocationNameMultiMap, so don't erase from TextVector //condition dropped at v1.1.4 because of change in EnterLocationNames
26226 /* else */ if(TextHandler->FindText(0, Name, HPos, VPos))
26227  {
26228  if(TextHandler->TextErase(4, HPos, VPos, Name))
26229  {
26230  ;
26231  } // condition not used
26232 
26233  TextFound = true;
26234  }
26235  Utilities->CallLogPop(1956);
26236  return(TextFound);
26237 }
26238 
26239 // ---------------------------------------------------------------------------
26240 
26241 void TInterface::AddLocationNameText(int Caller, AnsiString Name, int HPos, int VPos, bool UseEnteredPosition)
26242 {
26243  if(Name == "")
26244  {
26245  return;
26246  }
26247  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddLocationNameText," + Name + "," + AnsiString(HPos) + "," +
26248  AnsiString(VPos) + "," + AnsiString((short)UseEnteredPosition));
26249  int VPosHi, VPosLo, TextPosHi, TextPosLo;
26250  TFont *Font = Display->GetFont();
26251 
26252  if(!UseEnteredPosition)
26253  {
26254  if(!Track->FindHighestLowestAndLeftmostNamedElements(0, Name, VPosHi, VPosLo, HPos))
26255  {
26256  Utilities->CallLogPop(1561);
26257  return;
26258  }
26259  int Depth = abs(Font->Height); // Height may be negative - see C++Builder Help file
26260  TextPosHi = VPosHi + 20; // add depth of track element + 4 pixels
26261  TextPosLo = VPosLo - Depth - 4; // reduce by depth of font + 4 pixels
26262  int ScreenPosHi = (Display->DisplayOffsetV * 16) + 576;
26263  int ScreenPosLo = Display->DisplayOffsetV * 16;
26264  if(TextPosLo >= ScreenPosLo)
26265  {
26266  VPos = TextPosLo; // if Lo value on screen then use that - displays above the location
26267  }
26268  else if(TextPosHi < ScreenPosHi)
26269  {
26270  VPos = TextPosHi;
26271  }
26272  else
26273  {
26274  VPos = ScreenPosLo + 288; // if location extends to or beyond height of screen then display in centre of screen
26275  }
26276  }
26277  TTextItem TI(HPos, VPos, Name, Font);
26278 
26279  TI.Font = Font; // may have been changed in constructor when returned as reference
26280  TextHandler->EnterAndDisplayNewText(1, TI, HPos, VPos);
26281  Utilities->CallLogPop(1558);
26282 }
26283 
26284 // ---------------------------------------------------------------------------
26285 
26286 void TInterface::TestFunction() //triggered by Ctrl Alt 4
26287 {
26288  try
26289  {
26290  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TestFunction");
26291 
26292 //test code here
26293 
26294 
26295 //end of test code
26296 
26297  Utilities->CallLogPop(2376);
26298  }
26299  catch(const Exception &e)
26300  {
26301  ErrorLog(114, e.Message);
26302  }
26303 }
26304 
26305 // ---------------------------------------------------------------------------
26306 
26308 {
26309  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadNormalSignalGlyphs,");
26318  Utilities->CallLogPop(1871);
26319 }
26320 
26321 // ---------------------------------------------------------------------------
26322 
26323 void TInterface::LoadGroundSignalGlyphs(int Caller) // changed from the above at v2.3.0 so the signal glyphs change hands
26324 {
26325  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadGroundSignalGlyphs,");
26334  Utilities->CallLogPop(1872);
26335 }
26336 
26337 // ---------------------------------------------------------------------------
26338 
26339 void TInterface::UpdateActionsDuePanel(int Caller) // new at v2.2.0
26340 // limit it to 20 entries max
26341 {
26342  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateActionsDuePanel");
26344  {
26346  }
26348  // new at v2.2.0
26349  {
26350  Utilities->CallLogPop(2092);
26351  return;
26352  }
26353  AnsiString OpTimeToActDisplay;
26354  AnsiString OpTimeToActString;
26355  AnsiString HeadCode;
26356  float OpTimeToActFloat;
26357  TTrainController::THCandTrainPosParam HCandTrainPosParam;
26358 
26361  {
26362  if(ActionsDueForm->ActionsDueListBox->Items->Count >= 20)
26363  {
26364  break;
26365  }
26366  OpTimeToActFloat = TrainController->OpTimeToActMultiMapIterator->first;
26367  HCandTrainPosParam = TrainController->OpTimeToActMultiMapIterator->second;
26368  HeadCode = HCandTrainPosParam.first;
26369  //additions at v2.13.0 to show delayed trains in actions due list
26370  if(HCandTrainPosParam.second >= 0)
26371  {
26372  TTrain Train = TrainController->TrainVectorAtIdent(63, HCandTrainPosParam.second); //doesn't need to be a reference here
26373  if(int(Train.DelayedRandMins) > 0)
26374  {
26375  HeadCode += AnsiString('+'); //changed from 'd' to this on suggestion from Micke(Commuterpop) at v2.13.0
26376  }
26377  }
26378  //end of additions at v2.13.0
26379  if(OpTimeToActFloat < 0.25) // 15 secs estimated
26380  {
26381  OpTimeToActString = "NOW";
26382  }
26383  else if(OpTimeToActFloat < 1)
26384  {
26385  OpTimeToActString = "<1";
26386  }
26387  else
26388  {
26389  OpTimeToActString = AnsiString(floor(OpTimeToActFloat));
26390  }
26391  if(OpTimeToActFloat < 60)
26392  {
26393  OpTimeToActDisplay = HeadCode + AnsiString('\t') + OpTimeToActString;
26394  ActionsDueForm->ActionsDueListBox->Items->Add(OpTimeToActDisplay); // original
26395  }
26397  }
26398  Utilities->CallLogPop(2093);
26399 }
26400 
26401 // ---------------------------------------------------------------------------
26402 //below used in debugging TimeToExitMultiMap: replaces times to act in OpTimeToAct panel with headcodes and exit times (or exit locs) so they are visible
26403 //to use, uncomment this function and comment out the original above
26404 /*
26405 void TInterface::UpdateActionsDuePanel(int Caller) // new at v2.2.0
26406 // limit it to 20 entries max
26407 {
26408  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateActionsDuePanel");
26409  if(TrainController->OpActionPanelHintDelayCounter >= 60)
26410  {
26411  ActionsDueListBox->Clear();
26412  }
26413  if((!ActionsDuePanel->Visible) || TrainController->TimeToExitMultiMap.empty() || (TrainController->OpActionPanelHintDelayCounter < 60))
26414  // new at v2.2.0
26415  {
26416  Utilities->CallLogPop(2386);
26417  return;
26418  }
26419  AnsiString OpTimeToActDisplay;
26420  AnsiString OpTimeToActString;
26421  AnsiString HeadCode;
26422  THVShortPair ExitPair;
26423  float OpTimeToActFloat;
26424 // TTrainController::THCandTrainPosParam HCandTrainPosParam;
26425 
26426  TTimeToExitMultiMap::iterator TTEMMIt = TrainController->TimeToExitMultiMap.begin();
26427  while(TTEMMIt != TrainController->TimeToExitMultiMap.end())
26428  {
26429  if(ActionsDueListBox->Items->Count >= 20)
26430  {
26431  break;
26432  }
26433  OpTimeToActFloat = float(TTEMMIt->second.TimeToExitSecs);
26434  HeadCode = TTEMMIt->second.ServiceReference.Trim();
26435  ExitPair = TTEMMIt->first;
26436  if((OpTimeToActFloat > 0) && (OpTimeToActFloat < 3600)) //no display if outside these limits
26437  {
26438  OpTimeToActString = AnsiString(int(OpTimeToActFloat));//seconds
26439  OpTimeToActDisplay = HeadCode + AnsiString(' ') + OpTimeToActString; //comment out one of these to display times or exits
26440  ActionsDueListBox->Items->Add(OpTimeToActDisplay);
26441  }
26442  TTEMMIt++;
26443  }
26444  Utilities->CallLogPop(2377;
26445 }
26446 */
26447 // ---------------------------------------------------------------------------
26448 
26449 void TInterface::LoadUserGraphic(int Caller) // new at v2.4.0
26450 {
26451  try
26452  {
26453  TrainController->LogEvent("LoadUserGraphic");
26454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadUserGraphic");
26455  if(LoadUserGraphicDialog->Execute())
26456  {
26457  TrainController->LogEvent("LoadUserGraphic " + LoadUserGraphicDialog->FileName);
26458  SelectedGraphicFileName = AnsiString(LoadUserGraphicDialog->FileName); // SelectedGraphicFileName is a class member
26460  TTrack::TUserGraphicMap::iterator UGMIt = Track->UserGraphicMap.find(SelectedGraphicFileName);
26461  if(UGMIt == Track->UserGraphicMap.end()) // i.e. there isn't an entry for that filename so insert one, else take no action
26462  {
26463  UGME.first = SelectedGraphicFileName;
26464  TPicture *PicPtr = new TPicture;
26465  PicPtr->LoadFromFile(SelectedGraphicFileName);
26466  UGME.second = PicPtr;
26467  if(!Track->UserGraphicMap.insert(UGME).second) // if no failure then the new entry is inserted
26468  {
26469  throw Exception("Map Insertion Error 1 - UserGraphicMap insertion failure for " + SelectedGraphicFileName);
26470  }
26471  }
26473  SetLevel2TrackMode(65);
26474  }
26475  else //cancel
26476  {
26477  InfoPanel->Visible = false;
26478  }
26479  Utilities->CallLogPop(2191);
26480  }
26481  catch(const EInvalidGraphic &e) //non-error catch
26482  {
26483  ShowMessage(
26484  "Incorrect file format, the file can't be loaded.\nEnsure that the file you want is a valid graphic file with extension .bmp, .gif, .jpg, or .png");
26485  Utilities->CallLogPop(2311);
26486  }
26487  catch(const Exception &e)
26488  {
26489  ErrorLog(215, e.Message);
26490  }
26491 }
26492 
26493 // ---------------------------------------------------------------------------
26494 
26495 void TInterface::LoadClipboard(int Caller) // new at v2.8.0
26496 {
26497  try
26498  {
26499  TrainController->LogEvent("LoadClipboard");
26500  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadClipboard");
26501 
26502  std::wstringstream wss; // used to hold all parameters prior to conversion to a string buffer
26503  if(CopySelected)
26504  {
26505  wss << "RlyClpBrdCopy\n";
26506  }
26507  else
26508  {
26509  wss << "RlyClpBrd_Cut\n";
26510  }
26511 // Load the active & inactive track vectors
26512  for(TTrack::TTrackVectorIterator TTVIt = Track->SelectVector.begin(); TTVIt < Track->SelectVector.end(); TTVIt++)
26513  {
26514  wss << TTVIt->SpeedTag;
26515  wss << '\n'; // inherited int - all FixedTrackPiece parameters implicit in this
26516  for(int AnsLen = 0; AnsLen <= TTVIt->ActiveTrackElementName.Length(); AnsLen++)
26517  {
26518  if((TTVIt->ActiveTrackElementName).c_str()[AnsLen] != '\0')
26519  {
26520  wss << (TTVIt->ActiveTrackElementName).c_str()[AnsLen];
26521  }
26522  else
26523  {
26524  wss << '\n';
26525  }
26526  }
26527  for(int AnsLen = 0; AnsLen <= TTVIt->ElementID.Length(); AnsLen++)
26528  {
26529  if((TTVIt->ElementID).c_str()[AnsLen] != '\0')
26530  {
26531  wss << (TTVIt->ElementID).c_str()[AnsLen];
26532  }
26533  else
26534  {
26535  wss << '\n';
26536  }
26537  }
26538  for(int AnsLen = 0; AnsLen <= TTVIt->LocationName.Length(); AnsLen++)
26539  {
26540  if((TTVIt->LocationName).c_str()[AnsLen] != '\0')
26541  {
26542  wss << (TTVIt->LocationName).c_str()[AnsLen];
26543  }
26544  else
26545  {
26546  wss << '\n';
26547  }
26548  }
26549  wss << TTVIt->CallingOnSet;
26550  wss << '\n';
26551  wss << TTVIt->LCPlotted;
26552  wss << '\n';
26553 /* dropped at v2.13.0, not used from 2.12.0
26554  wss << TTVIt->TempTrackMarker01;
26555  wss << '\n';
26556  wss << TTVIt->TempTrackMarker23;
26557  wss << '\n';
26558 */
26559  wss << TTVIt->Attribute;
26560  wss << '\n'; // all ints from here except last which is an enum
26561  wss << TTVIt->Conn[0];
26562  wss << '\n';
26563  wss << TTVIt->Conn[1];
26564  wss << '\n';
26565  wss << TTVIt->Conn[2];
26566  wss << '\n';
26567  wss << TTVIt->Conn[3];
26568  wss << '\n';
26569  wss << TTVIt->ConnLinkPos[0];
26570  wss << '\n';
26571  wss << TTVIt->ConnLinkPos[1];
26572  wss << '\n';
26573  wss << TTVIt->ConnLinkPos[2];
26574  wss << '\n';
26575  wss << TTVIt->ConnLinkPos[3];
26576  wss << '\n';
26577  wss << TTVIt->HLoc;
26578  wss << '\n';
26579  wss << TTVIt->VLoc;
26580  wss << '\n';
26581  wss << TTVIt->Length01;
26582  wss << '\n';
26583  wss << TTVIt->Length23;
26584  wss << '\n';
26585  wss << TTVIt->SpeedLimit01;
26586  wss << '\n';
26587  wss << TTVIt->SpeedLimit23;
26588  wss << '\n';
26589  wss << TTVIt->StationEntryStopLinkPos1;
26590  wss << '\n';
26591  wss << TTVIt->StationEntryStopLinkPos2;
26592  wss << '\n';
26593  wss << TTVIt->StationEntryStopLinkPos3;
26594  wss << '\n';
26595  wss << TTVIt->StationEntryStopLinkPos4;
26596  wss << '\n';
26597  wss << TTVIt->TrainIDOnElement;
26598  wss << '\n';
26599  wss << TTVIt->TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
26600  wss << '\n';
26601  wss << TTVIt->TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
26602  wss << '\n';
26603  wss << int(TTVIt->SigAspect);
26604  wss << '\n'; // enum
26605  }
26606  wss << "$$$" << '\n'; // send track element end marker
26607 
26608 // Load the text vector
26609 
26610  for(TTextHandler::TTextVectorIterator TTVIt = TextHandler->SelectTextVector.begin(); TTVIt < TextHandler->SelectTextVector.end(); TTVIt++)
26611  {
26612  for(int AnsLen = 0; AnsLen <= TTVIt->TextString.Length(); AnsLen++)
26613  {
26614  if((TTVIt->TextString).c_str()[AnsLen] != '\0')
26615  {
26616  wss << (TTVIt->TextString).c_str()[AnsLen];
26617  }
26618  else
26619  {
26620  wss << '\n';
26621  }
26622  }
26623  wss << TTVIt->HPos;
26624  wss << '\n';
26625  wss << TTVIt->VPos;
26626  wss << '\n';
26627  for(int AnsLen = 0; AnsLen <= AnsiString(TTVIt->Font->Name).Length(); AnsLen++)
26628  {
26629  if(AnsiString(TTVIt->Font->Name).c_str()[AnsLen] != '\0')
26630  {
26631  wss << AnsiString(TTVIt->Font->Name).c_str()[AnsLen];
26632  }
26633  else
26634  {
26635  wss << '\n';
26636  }
26637  }
26638  wss << TTVIt->Font->Size;
26639  wss << '\n';
26640  if((TTVIt->Font->Color < 0) || (TTVIt->Font->Color > 0xFFFFFF)) // if set to any of the special 'windows' colours save it as black
26641  {
26642  wss << "0\n";
26643  }
26644  else
26645  {
26646  wss << int(TTVIt->Font->Color) << '\n';
26647  }
26648  wss << int(TTVIt->Font->Charset) << '\n'; // save as 'int' (would be unsigned char else) so 'n' can act as proper delimiter
26649  wss << TextHandler->GetFontStyleAsInt(1, TTVIt->Font) << '\n';
26650  }
26651  wss << "$$$" << '\n'; // send text item end marker
26652 
26653  //load select dimensions
26654  wss << SelectBitmap->Height;
26655  wss << '\n';
26656  wss << SelectBitmap->Width;
26657  wss << '\n';
26658  wss << SelectRect.left;
26659  wss << '\n';
26660  wss << SelectRect.top;
26661  wss << '\n';
26662  wss << "$$$" << '\n'; // send end of select dimension marker
26663 
26664  //load preferred directions //added at v2.9.0
26665  if(SelectPrefDir->PrefDirSize() > 0) // skip load if empty
26666  {
26667  for(TOnePrefDir::TPrefDirVectorIterator PDVIt = SelectPrefDir->PrefDirVector.begin(); PDVIt < SelectPrefDir->PrefDirVector.end(); PDVIt++)
26668  {
26669  //Note that TrackVector Position won't be valid for a remote paste, it will be reset when pasted, also Conns & ConnLinkPosses set when
26670  //track linked
26671  wss << PDVIt->GetTrackVectorPosition(); //added at v2.9.2 so all 9 of CheckCount properties loaded (SpeedTag loaded from TrackElement at HLoc & VLoc)
26672  wss << '\n';
26673  wss << PDVIt->GetHLoc();
26674  wss << '\n';
26675  wss << PDVIt->GetVLoc();
26676  wss << '\n';
26677  wss << PDVIt->GetELink();
26678  wss << '\n';
26679  wss << PDVIt->GetELinkPos();
26680  wss << '\n';
26681  wss << PDVIt->GetXLink();
26682  wss << '\n';
26683  wss << PDVIt->GetXLinkPos();
26684  wss << '\n';
26685  wss << PDVIt->GetEXNumber();
26686  wss << '\n';
26687  }
26688  }
26689  wss << "$$$" << '\n'; // send pref dir end marker
26690  wss << '\0'; // has to end with NULL
26691 
26692  Clipboard()->Clear(); // clear the clipboard
26693  Clipboard()->SetTextBuf(&(wss.str()[0])); // populate the clipboard
26694  Clipboard()->Close();
26695 
26696  Utilities->CallLogPop(2267);
26697  }
26698 
26699  catch(const EClipboardException &e) //non-error catch - ignore access denials (but only seems to happen with recover), doesn't affect program
26700  {
26701 // Application->MessageBox(L"A clipboard error occurred in loading the clipboard", L"Message", MB_OK);
26702  TrainController->LogEvent("EClipboardException in LoadClipboard - message = " + e.Message);
26703  Utilities->CallLogPop(2312);
26704  }
26705 
26706  catch(const Exception &e)
26707  {
26708  ErrorLog(222, e.Message);
26709  }
26710 
26711 }
26712 
26713 // ---------------------------------------------------------------------------
26714 
26715 void TInterface::RecoverClipboard(int Caller, bool &ValidResult) // new at v2.8.0
26716 {
26717  try
26718  {
26719  TrainController->LogEvent("RecoverClipboard");
26720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RecoverClipboard");
26721  ValidResult = false;
26722  System::WideChar *SelectVectorBuffer = new System::WideChar[4000000]; // scope for 130 chars per element & 4k resolution (240 x 135 elements)
26723  int StreamSize = 0;
26724  StreamSize = Clipboard()->GetTextBuf(SelectVectorBuffer, 4000000);
26725  Clipboard()->Clear(); // clear it so can't keep pasting same thing as don't permit this in single app
26726  Clipboard()->Close();
26727  if(StreamSize < 14)
26728  {
26729  Utilities->CallLogPop(2270); // ValidResult == false
26730  return;
26731  }
26732  std::wstringstream wss;
26733  wss << SelectVectorBuffer;
26734  ClpBrdValid = AnsiString(SelectVectorBuffer).SubString(1, 13);
26735  PasteMenuItem->Enabled = false;
26736  delete[]SelectVectorBuffer;
26737 
26738  if((ClpBrdValid != AnsiString("RlyClpBrdCopy")) && (ClpBrdValid != AnsiString("RlyClpBrd_Cut")))
26739  {
26740  Utilities->CallLogPop(2268); // ValidResult == false
26741  return;
26742  }
26743  int MarkerCounter = 0; //If ever get a MarkerCounter value well outside expected range it's probably because some text is too long - see comment below
26744  ClpBrdValid = "";
26745  wchar_t LineString[1000]; // should be big enough for any entry, (extended from 100 at v2.9.2) text can be long but hopefully not this long - won't
26746  //recover the clipboard if longer but won't crash. It has to be at least as big as the biggest getline number or the excess will overwrite other variables,
26747  //this was discovered when MarkerCounter suddenly jumped from 1 to nearly 3000000 when Linesting loaded with text ~180 chars long with LineString limited
26748  //to 100 chars
26749 
26750  wss.getline(LineString, 100); // RlyClpBrdCopy or ...Cut - discard it
26751  Track->SelectVector.clear();
26752  TTrack::TTrackMap SelectTrackMap; //build map so can find TrackElement at required H & V to build PrefDirElement from it //added at v2.9.0
26753  THVShortPair SelectTrackMapKeyPair; //for above
26754  TTrack::TTrackMapEntry SelectTrackMapEntry; //for above
26755  while(true) // recover active & inactive track (active first & top to bottom at leftmost position then again stepping right
26756  {
26757  wss.getline(LineString, 100);
26758  if(AnsiString(LineString) == "$$$") // end of track element marker
26759  {
26760  MarkerCounter++;
26761  break;
26762  }
26763  // if not $$$ then it's the SpeedTag number
26764  TTrackElement TE = Track->BuildBasicElementFromSpeedTag(5, AnsiString(LineString).ToInt());
26765  wss.getline(LineString, 100);
26766  TE.ActiveTrackElementName = AnsiString(LineString); // if not "$$$" must be the start of the next element
26767  wss.getline(LineString, 100);
26768  TE.ElementID = AnsiString(LineString);
26769  wss.getline(LineString, 100);
26770  TE.LocationName = AnsiString(LineString);
26771 
26772  wss.getline(LineString, 100);
26773  TE.CallingOnSet = AnsiString(LineString).ToInt();
26774  wss.getline(LineString, 100);
26775  TE.LCPlotted = AnsiString(LineString).ToInt();
26776 /* dropped at v2.13.0, not used from 2.12.0
26777  wss.getline(LineString, 100);
26778  TE.TempTrackMarker01 = AnsiString(LineString).ToInt();
26779  wss.getline(LineString, 100);
26780  TE.TempTrackMarker23 = AnsiString(LineString).ToInt();
26781 */
26782  wss.getline(LineString, 100);
26783  TE.Attribute = AnsiString(LineString).ToInt();
26784  wss.getline(LineString, 100);
26785  TE.Conn[0] = AnsiString(LineString).ToInt();
26786  wss.getline(LineString, 100);
26787  TE.Conn[1] = AnsiString(LineString).ToInt();
26788  wss.getline(LineString, 100);
26789  TE.Conn[2] = AnsiString(LineString).ToInt();
26790  wss.getline(LineString, 100);
26791  TE.Conn[3] = AnsiString(LineString).ToInt();
26792  wss.getline(LineString, 100);
26793  TE.ConnLinkPos[0] = AnsiString(LineString).ToInt();
26794  wss.getline(LineString, 100);
26795  TE.ConnLinkPos[1] = AnsiString(LineString).ToInt();
26796  wss.getline(LineString, 100);
26797  TE.ConnLinkPos[2] = AnsiString(LineString).ToInt();
26798  wss.getline(LineString, 100);
26799  TE.ConnLinkPos[3] = AnsiString(LineString).ToInt();
26800  wss.getline(LineString, 100);
26801  TE.HLoc = AnsiString(LineString).ToInt();
26802  wss.getline(LineString, 100);
26803  TE.VLoc = AnsiString(LineString).ToInt();
26804  wss.getline(LineString, 100);
26805  TE.Length01 = AnsiString(LineString).ToInt();
26806  wss.getline(LineString, 100);
26807  TE.Length23 = AnsiString(LineString).ToInt();
26808  wss.getline(LineString, 100);
26809  TE.SpeedLimit01 = AnsiString(LineString).ToInt();
26810  wss.getline(LineString, 100);
26811  TE.SpeedLimit23 = AnsiString(LineString).ToInt();
26812  wss.getline(LineString, 100);
26813  TE.StationEntryStopLinkPos1 = AnsiString(LineString).ToInt();
26814  wss.getline(LineString, 100);
26815  TE.StationEntryStopLinkPos2 = AnsiString(LineString).ToInt();
26816  wss.getline(LineString, 100);
26817  TE.StationEntryStopLinkPos3 = AnsiString(LineString).ToInt();
26818  wss.getline(LineString, 100);
26819  TE.StationEntryStopLinkPos4 = AnsiString(LineString).ToInt();
26820  wss.getline(LineString, 100);
26821  TE.TrainIDOnElement = AnsiString(LineString).ToInt();
26822  wss.getline(LineString, 100);
26823  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = AnsiString(LineString).ToInt();
26824  wss.getline(LineString, 100);
26825  TE.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = AnsiString(LineString).ToInt();
26826 
26827  wss.getline(LineString, 100);
26828  int temp = AnsiString(LineString).ToInt();
26829  if(temp == 0)
26830  {
26832  }
26833  else if(temp == 1)
26834  {
26836  }
26837  else if(temp == 2)
26838  {
26840  }
26841  else if(temp == 3)
26842  {
26844  }
26845  Track->SelectVector.push_back(TE);
26846  if((TE.TrackType != Concourse) && (TE.TrackType != Parapet) && (TE.TrackType != NamedNonStationLocation) && (TE.TrackType != Platform)
26847  && (TE.TrackType != LevelCrossing)) //aded at v2.9.2 so only active elements added to SelectTrackMap
26848  {
26849  SelectTrackMapKeyPair.first = TE.HLoc;
26850  SelectTrackMapKeyPair.second = TE.VLoc;
26851  SelectTrackMapEntry.first = SelectTrackMapKeyPair;
26852  SelectTrackMapEntry.second = Track->SelectVector.size() - 1;
26853  SelectTrackMap.insert(SelectTrackMapEntry);
26854  }
26855  }
26856 
26857  TextHandler->SelectTextVector.clear();
26858  AnsiString FontName;
26859  int FontSize, FontColour, FontCharset, FontStyle;
26860  while(true) // recover text
26861  {
26862  wss.getline(LineString, 1000);
26863  if(AnsiString(LineString) == "$$$") // end of text marker
26864  {
26865  MarkerCounter++;
26866  break;
26867  }
26868  // if not $$$ then it's the text string
26869  TTextItem TI;
26870  TI.TextString = AnsiString(LineString);
26871  wss.getline(LineString, 1000); //extended to 1000 from 100 for text items at v2.9.2
26872  TI.HPos = AnsiString(LineString).ToInt();
26873  wss.getline(LineString, 1000);
26874  TI.VPos = AnsiString(LineString).ToInt();
26875  wss.getline(LineString, 1000);
26876  FontName = AnsiString(LineString).c_str();
26877  wss.getline(LineString, 1000);
26878  FontSize = AnsiString(LineString).ToInt();
26879  wss.getline(LineString, 1000);
26880  FontColour = AnsiString(LineString).ToInt();
26881  wss.getline(LineString, 1000);
26882  FontCharset = AnsiString(LineString).ToInt();
26883  wss.getline(LineString, 1000);
26884  FontStyle = AnsiString(LineString).ToInt();
26885  // create a new font
26886  TFont *NewFont = new TFont;
26887  NewFont->Name = FontName;
26888  NewFont->Size = FontSize;
26889  NewFont->Color = static_cast<TColor>(FontColour);
26890  NewFont->Charset = FontCharset;
26891  NewFont->Style = TextHandler->SetFontStyleFromInt(1, FontStyle);
26892  TI.Font = NewFont;
26893  TextHandler->SelectTextVector.push_back(TI);
26894  }
26895 
26896  // recover select dimensions
26897  wss.getline(LineString, 100);
26898  SelectBitmap->Height = AnsiString(LineString).ToInt();
26899  wss.getline(LineString, 100);
26900  SelectBitmap->Width = AnsiString(LineString).ToInt();
26901  wss.getline(LineString, 100);
26902  SelectRect.left = AnsiString(LineString).ToInt();
26903  wss.getline(LineString, 100);
26904  SelectRect.top = AnsiString(LineString).ToInt();
26905  wss.getline(LineString, 100);
26906  if(AnsiString(LineString) == "$$$")
26907  {
26908  MarkerCounter++;
26909  }
26910 
26911  // recover pref dirs - after dimensions so that a clipboard loaded with this app will paste into an earlier version app without pref dirs
26912  // if a clipboard loaded with an earlier app is pasted into this app then the marker will be wrong, an error message will be given but no crash
26913  int TempTVPos, TempHLoc, TempVLoc, TempELink, TempELinkPos, TempXLink, TempXLinkPos, TempEXNumber, ATVecPos;
26914  bool FoundFlag;
26916  while(true)
26917  {
26918  wss.getline(LineString, 100);
26919  if(AnsiString(LineString) == "$$$") // end of pref dir element marker
26920  {
26921  MarkerCounter++;
26922  break;
26923  }
26924  // if not $$$ then it's the TVPos value
26925  TempTVPos = AnsiString(LineString).ToInt(); //added at v2.9.2 so all 9 CheckCount properties valid (SpeedTag loaded from corresponding TrackElement)
26926  wss.getline(LineString, 100);
26927  TempHLoc = AnsiString(LineString).ToInt();
26928  wss.getline(LineString, 100);
26929  TempVLoc = AnsiString(LineString).ToInt();
26930  wss.getline(LineString, 100);
26931  TempELink = AnsiString(LineString).ToInt();
26932  wss.getline(LineString, 100);
26933  TempELinkPos = AnsiString(LineString).ToInt();
26934  wss.getline(LineString, 100);
26935  TempXLink = AnsiString(LineString).ToInt();
26936  wss.getline(LineString, 100);
26937  TempXLinkPos = AnsiString(LineString).ToInt();
26938  wss.getline(LineString, 100);
26939  TempEXNumber = AnsiString(LineString).ToInt();
26940  //build a pref dir element from these values and the corresponding TrackVectorPosition for HLoc & VLoc
26941  TTrackElement TempElement = Track->GetTrackElementFromAnyTrackMap(0, TempHLoc, TempVLoc, SelectTrackMap, Track->SelectVector);
26942  TPrefDirElement TempPrefDirElement(TempElement);
26943 // TempPrefDirElement.HLoc = TempHLoc; //these and SpeedTag already set from TempElement
26944 // TempPrefDirElement.VLoc = TempVLoc;
26945  TempPrefDirElement.SetTrackVectorPosition(TempTVPos); //added at v2.9.2 so all 9 CheckCount properties have values (SpeedTag loaded from corresponding TrackElement)
26946  //TVPos only included for completeness & not valid yet, it will change to the correct value when pasted
26947  //and change again when the track is linked
26948  TempPrefDirElement.SetELink(TempELink);
26949  TempPrefDirElement.SetELinkPos(TempELinkPos);
26950  TempPrefDirElement.SetXLink(TempXLink);
26951  TempPrefDirElement.SetXLinkPos(TempXLinkPos);
26952  TempPrefDirElement.SetEXNumber(TempEXNumber);
26953  TempPrefDirElement.SetCheckCount(9); //added at v2.9.2
26954  SelectPrefDir->ExternalStorePrefDirElement(11, TempPrefDirElement); //added 27/05 at v2.9.0
26955  }
26956 
26957  if(MarkerCounter == 4)
26958  {
26959  ValidResult = true;
26960  }
26961  Utilities->CallLogPop(2269);
26962  }
26963 
26964  catch(const EClipboardException &e) //non-error catch - don't stop for any error in this section, just give bad clipboard message
26965  {
26966  ValidResult = false;
26967  TrainController->LogEvent("EClipboardException in RecoverClipboard - message = " + e.Message);
26968  Utilities->CallLogPop(2313);
26969  }
26970  catch(const Exception &e) //non-error catch - non-clipboard exception
26971  {
26972  ValidResult = false;
26973  TrainController->LogEvent("non-clipboard exception in RecoverClipboard - message = " + e.Message);
26974  Utilities->CallLogPop(2322);
26975 // ErrorLog(223, e.Message);
26976  }
26977 
26978 }
26979 // ---------------------------------------------------------------------------
26980 
26981 void __fastcall TInterface::NoDelaysMenuItemClick(TObject *Sender) //these added at v2.13.0
26982 {
26983  NoDelaysMenuItem->Enabled = false;
26984  MinorDelaysMenuItem->Enabled = true;
26985  ModerateDelaysMenuItem->Enabled = true;
26986  MajorDelaysMenuItem->Enabled = true;
26987  DelayMenu->Caption = "No delays";
26988  PerfLogForm->PerformanceLog(32, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": No random delays selected");
26989  Utilities->DelayMode = Nil;
26990 }
26991 //---------------------------------------------------------------------------
26992 
26993 void __fastcall TInterface::MinorDelaysMenuItemClick(TObject *Sender)
26994 {
26995  NoDelaysMenuItem->Enabled = true;
26996  MinorDelaysMenuItem->Enabled = false;
26997  ModerateDelaysMenuItem->Enabled = true;
26998  MajorDelaysMenuItem->Enabled = true;
26999  DelayMenu->Caption = "Minor delays";
27000  PerfLogForm->PerformanceLog(33, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Minor random delays selected");
27002 }
27003 //---------------------------------------------------------------------------
27004 
27005 void __fastcall TInterface::ModerateDelaysMenuItemClick(TObject *Sender)
27006 {
27007  NoDelaysMenuItem->Enabled = true;
27008  MinorDelaysMenuItem->Enabled = true;
27009  ModerateDelaysMenuItem->Enabled = false;
27010  MajorDelaysMenuItem->Enabled = true;
27011  DelayMenu->Caption = "Moderate delays";
27012  PerfLogForm->PerformanceLog(34, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Moderate random delays selected");
27014 }
27015 //---------------------------------------------------------------------------
27016 
27017 void __fastcall TInterface::MajorDelaysMenuItemClick(TObject *Sender)
27018 {
27019  NoDelaysMenuItem->Enabled = true;
27020  MinorDelaysMenuItem->Enabled = true;
27021  ModerateDelaysMenuItem->Enabled = true;
27022  MajorDelaysMenuItem->Enabled = false;
27023  DelayMenu->Caption = "Major delays";
27024  PerfLogForm->PerformanceLog(35, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Major random delays selected");
27026 }
27027 
27028 // ---------------------------------------------------------------------------
27029 
27030 void __fastcall TInterface::NoFailuresMenuItemClick(TObject *Sender)
27031 {
27032  NoFailuresMenuItem->Enabled = false;
27033  MinorFailuresMenuItem->Enabled = true;
27034  ModerateFailuresMenuItem->Enabled = true;
27035  MajorFailuresMenuItem->Enabled = true;
27039  FailureMenu->Caption = "No failures";
27040  PerfLogForm->PerformanceLog(61, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": No random failures selected");
27042 }
27043 
27044 //---------------------------------------------------------------------------
27045 
27046 void __fastcall TInterface::MinorFailuresMenuItemClick(TObject *Sender)
27047 {
27048  NoFailuresMenuItem->Enabled = true;
27049  MinorFailuresMenuItem->Enabled = false;
27050  ModerateFailuresMenuItem->Enabled = true;
27051  MajorFailuresMenuItem->Enabled = true;
27055  FailureMenu->Caption = "Minor failures";
27056  PerfLogForm->PerformanceLog(62, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Minor random failures selected");
27058 }
27059 
27060 //---------------------------------------------------------------------------
27061 
27062 void __fastcall TInterface::ModerateFailuresMenuItemClick(TObject *Sender)
27063 {
27064  NoFailuresMenuItem->Enabled = true;
27065  MinorFailuresMenuItem->Enabled = true;
27066  ModerateFailuresMenuItem->Enabled = false;
27067  MajorFailuresMenuItem->Enabled = true;
27071  FailureMenu->Caption = "Moderate failures";
27072  PerfLogForm->PerformanceLog(63, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Moderate random failures selected");
27074 }
27075 
27076 //---------------------------------------------------------------------------
27077 
27078 void __fastcall TInterface::MajorFailuresMenuItemClick(TObject *Sender)
27079 {
27080  NoFailuresMenuItem->Enabled = true;
27081  MinorFailuresMenuItem->Enabled = true;
27082  ModerateFailuresMenuItem->Enabled = true;
27083  MajorFailuresMenuItem->Enabled = false;
27087  FailureMenu->Caption = "Major failures";
27088  PerfLogForm->PerformanceLog(64, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Major random failures selected");
27090 }
27091 
27092 //---------------------------------------------------------------------------
27093 
27094 void __fastcall TInterface::FlashControlButtonClick(TObject *Sender) //added at v2.15.0
27095 {
27096  if(StopFlashFlag)
27097  {
27098  StopFlashFlag = false;
27099  FlashControlButton->Glyph->LoadFromResourceName(0, "NoFlash");
27100  }
27101  else
27102  {
27103  StopFlashFlag = true;
27104  FlashControlButton->Glyph->LoadFromResourceName(0, "Flash");
27105  }
27106 }
27107 
27108 //---------------------------------------------------------------------------
27109 
27110 bool TInterface::GetTrainIDOrContinuationPosition(int Caller, int X, int Y, int &TrainID, int &TrackVectorPosition)
27111 // returns true if value(s) valid
27112 {
27113  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",GetTrainIDOrContinuationPosition");
27115 // int TopPos = ActionsDueForm->ActionsDueListBox->TopIndex;
27116 
27117 //substituted for the below
27118  int OAIndex = ActionsDueForm->ActionsDueListBox->ItemAtPos(TPoint(X,Y), true);
27119  if(OAIndex == -1)
27120  {
27121  Utilities->CallLogPop(2089);
27122  return(false);
27123  }
27124  else
27125  {
27126  OACurrentEntryPtr = TrainController->OpTimeToActMultiMap.begin();
27127  std::advance(OACurrentEntryPtr, OAIndex);
27128  }
27129 //dropped at v2.13.0 as unreliable on high resolution monitors
27130 /* if((TopPos + (Y / 13)) >= ActionsDueForm->ActionsDueListBox->Items->Count) // if click beyond end of list ignore
27131  {
27132  Utilities->CallLogPop();
27133  return(false);
27134  }
27135  else
27136  {
27137  OACurrentEntryPtr = TrainController->OpTimeToActMultiMap.begin();
27138  std::advance(OACurrentEntryPtr, ((Y / 13) + TopPos));
27139  }
27140 */
27141 
27142  int TrainIDorTVPos = OACurrentEntryPtr->second.second;
27143  if(TrainIDorTVPos >= 0) // running train, so value is the TrainID
27144  {
27145  if(TrainController->TrainExistsAtIdent(0, TrainIDorTVPos)) // added at v2.4.0 in case train removed but still in OA list as not updated yet
27146  // see LiWinDom error report on Discord 23/04/20. Also needed for click ActionsDueListBox before any trains show,
27147  // as notified by Rokas Serys by email on 16/05/20
27148  {
27149  TrainID = TrainIDorTVPos;
27150  TrackVectorPosition = TrainController->TrainVectorAtIdent(43, TrainIDorTVPos).GetLeadElement();
27151  }
27152  else
27153  {
27154  Utilities->CallLogPop(2155); // if not there then ignore
27155  return(false);
27156  }
27157  }
27158  else // train to enter at a continuation, so value is -TVPos of continuation - 1
27159  {
27160  TrackVectorPosition = -(TrainIDorTVPos + 1);
27161  }
27162  Utilities->CallLogPop(2261);
27163  return(true);
27164 }
27165 
27166 // ---------------------------------------------------------------------------
27167 //Multiplayer Code
27168 // ---------------------------------------------------------------------------
27169 
27170 // ---------------------------------------------------------------------------
27171 //Host functions
27172 // ---------------------------------------------------------------------------
27173 
27174 void __fastcall TInterface::MultiplayerHostSessionMenuItemClick(TObject *Sender)
27175 {
27176  try
27177  {
27178  TrainController->LogEvent("MultiplayerHostSessionMenuItemClick");
27179  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MultiplayerHostSessionMenuItemClick");
27180  MultiplayerHostPanel->Visible = true;
27181  MultiplayerHostPanel->Left = MainScreen->Left + (MainScreen->Width / 2) - (MultiplayerHostPanel->Width / 2);
27182  MultiplayerHostPanel->Top = MainScreen->Top + 100;
27183 
27184  MultiplayerHostSessionMenuItem->Enabled = false;
27185  SaveMultiplayerSessionMenuItem->Enabled = false;
27186  EndSimulationMenuItem->Enabled = false;
27187  ShowHideStringGridMenuItem->Enabled = false;
27188  JoinMultiplayerSessionMenuItem->Enabled = false;
27189  ExitSimulationMenuItem->Enabled = false;
27190  HostInSessionFlag = false;
27191  CouplingFileLoadedFlag = false;
27193  PlayerReadyToBeginFlag = false;
27194  PlayerCancelJoinFlag = false;
27196  PlayerInSessionFlag = false;
27197 
27198  MPHPLoadCouplingFileButton->Enabled = false;
27199  MPHPCancelButton->Enabled = true;
27200  MPHPStartButton->Enabled = false;
27201  MPHPGeneralLabel->Caption = "Complete the boxes and press 'Enter' for each.";
27202  MPHPOwnIPEditBox->Text = "0.0.0.0";// <--temporary - change to "" for release
27203  MPHPOwnPortEditBox->Text = "50000";// <--temporary - change to "" for release
27204  MPHPOwnIPEditBox->Enabled = true;
27205  MPHPOwnPortEditBox->Enabled = true;
27206  MPHostClient->Active = false;
27207  Utilities->CallLogPop(2348);
27208  }
27209 
27210  catch(const EIdException &e) //non-error catch
27211  {
27212  ShowMessage("MultiplayerHostSessionMenuItemClick " + e.Message); //<--temporary
27213  Utilities->CallLogPop(2407);
27214  }
27215  catch(const Exception &e)
27216  {
27217  ErrorLog(226, e.Message);
27218  }
27219 }
27220 
27221 //---------------------------------------------------------------------------
27222 
27223 void __fastcall TInterface::MPHPLoadCouplingFileButtonClick(TObject *Sender)
27224 {
27225  try
27226  {
27227  TrainController->LogEvent("MPHPLoadCouplingFileButtonClick");
27228  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPHPLoadCouplingFileButtonClick");
27229  LoadCouplingFileDialog->Filter = "Text file (*.txt)|*.txt";
27230  if(LoadCouplingFileDialog->Execute()) //error messages given in PopulateCouplingMap
27231  {
27232  NumPlayers = 0;
27233  TrainController->LogEvent("LoadCouplingFile " + AnsiString(LoadCouplingFileDialog->FileName));
27234  if(PopulateCouplingMap(AnsiString(LoadCouplingFileDialog->FileName), NumPlayers))
27235  {
27236  MultiplayerHostStringGrid->RowCount = 1; //clear it before loading or reloading
27237  MultiplayerHostStringGrid->ColCount = 1;
27238  MultiplayerHostStringGrid->Cells[0][0] = "";
27240  MultiplayerHostStringGrid->Width = 260;
27242  MultiplayerHostStringGrid->Height = (25 * NumPlayers) + 54;
27243  if(MultiplayerHostStringGrid->Height > MainScreen->Height - 100)
27244  {
27245  MultiplayerHostStringGrid->Height = MainScreen->Height - 120;
27246  }
27247  MultiplayerHostStringGrid->ColCount = 3;
27248  MultiplayerHostStringGrid->RowCount = NumPlayers + 2; //2 = heading & blank row
27249  MultiplayerHostStringGrid->ColWidths[1] = 128;
27250  MultiplayerHostStringGrid->Cells[0][0] = "Player";
27251  MultiplayerHostStringGrid->Cells[1][0] = "Railway";
27252  MultiplayerHostStringGrid->Cells[2][0] = "Ready?";
27253  bool RailwayFound = false;
27254  for(RLIt = RailwayList.begin(); RLIt != RailwayList.end(); RLIt++)
27255  {
27256  if(*RLIt != RailwayTitle) //host's railway
27257  {
27258  continue;
27259  }
27260  else
27261  {
27262  RailwayFound = true;
27263  break;
27264  }
27265  }
27266  if(!RailwayFound)
27267  {
27268  ShowMessage("Can't find '" + RailwayTitle + "' in coupling file - please load an appropriate railway and timetable or a pre-start session file");
27269  Utilities->CallLogPop(2349);
27270  return;
27271  }
27272  MultiplayerHostStringGrid->Visible = true;
27273  MPHPLoadCouplingFileButton->Enabled = false;
27274  MPHPGeneralLabel->Caption = "Awaiting player connections";
27275  int GridCount = 2; ;//row 1 is blank
27276  InfoVector.clear();
27277  TRlyUserInfo RlyUserInfo; //has default values
27278  unsigned char x = 1; //numbers start at 1
27279  //populate StringGrid & InfoVector with RailwayNames & numbers + "Host" as user in correct place
27280  for(RLIt = RailwayList.begin(); RLIt != RailwayList.end(); RLIt++)//railways in order
27281  {
27282  if(*RLIt == RailwayTitle) //host's railway
27283  {
27284  MultiplayerHostStringGrid->Cells[0][GridCount] = "Host";
27285  MultiplayerHostStringGrid->Cells[1][GridCount] = *RLIt; //RailwayName
27286  MultiplayerHostStringGrid->Cells[2][GridCount] = "Yes";
27287  RlyUserInfo.RailwayName = *RLIt; //RailwayName
27288  RlyUserInfo.UserName = "Host";
27289  RlyUserInfo.RlyUserNumber = x;
27290  InfoVector.push_back(RlyUserInfo);
27291  x++;
27292  }
27293  else
27294  {
27295  MultiplayerHostStringGrid->Cells[0][GridCount] = ""; //UserName
27296  MultiplayerHostStringGrid->Cells[1][GridCount] = *RLIt; //RailwayName
27297  MultiplayerHostStringGrid->Cells[2][GridCount] = "No";
27298  RlyUserInfo.RailwayName = *RLIt; //RailwayName
27299  RlyUserInfo.RlyUserNumber = x;
27300  InfoVector.push_back(RlyUserInfo);
27301  x++;
27302  }
27303  GridCount++;
27304  }
27305  CouplingFileLoadedFlag = true;
27306  MPHostClient->Active = true;
27307  }
27308  //no need for 'else' - messages given in called functions
27309  }
27310  Utilities->CallLogPop(2324);
27311  }
27312  catch(const EIdException &e) //non-error catch
27313  {
27314  ShowMessage("MPHPLoadCouplingFileButtonClick " + e.Message); //<--temporary
27315  Utilities->CallLogPop(2408);
27316  }
27317  catch(const Exception &e)
27318  {
27319  ErrorLog(225, e.Message);
27320  }
27321 }
27322 
27323 //---------------------------------------------------------------------------
27324 
27325 void __fastcall TInterface::MPHPCancelButtonClick(TObject *Sender)
27326 {
27327  try
27328  {
27329  TrainController->LogEvent("MPHPCancelButtonClick");
27330  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPHPCancelButtonClick");
27331  MPHostClient->Active = false;
27332  MPHPOwnIPEditBox->Text = "";
27333  MPHPOwnPortEditBox->Text = "";
27334  MultiplayerHostPanel->Visible = false;
27335  MultiplayerHostStringGrid->Visible = false;
27336  MPHPStartButton->Enabled = false;
27337 // AnsiCouplingMap.clear(); cleared in PopulateCouplingMap
27338  CouplingFileLoadedFlag = false;
27339  HostInSessionFlag = false;
27340  Utilities->CallLogPop(2350);
27341  }
27342  catch(const EIdException &e) //non-error catch
27343  {
27344  ShowMessage("MPHPCancelButtonClick " + e.Message); //<--temporary
27345  Utilities->CallLogPop(2409);
27346  }
27347  catch(const Exception &e)
27348  {
27349  ErrorLog(227, e.Message);
27350  }
27351 }
27352 
27353 //---------------------------------------------------------------------------
27354 
27355 void __fastcall TInterface::MPHPStartButtonClick(TObject *Sender)
27356 {
27357  try
27358  {
27359  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPHPStartButtonClick");
27360 
27361  ShowMessage("We're off!"); //<--temporary
27362  MultiplayerHostPanel->Visible = false;
27363  MultiplayerHostStringGrid->Visible = false;
27364  HostInSessionFlag = true;
27365  OperateButton->Click(); //start own session
27366  Utilities->CallLogPop(2369);
27367  }
27368  catch(const EIdException &e) //non-error catch
27369  {
27370  ShowMessage("MPHPStartButtonClick " + e.Message); //<--temporary
27371  Utilities->CallLogPop(2410);
27372  }
27373  catch(const Exception &e)
27374  {
27375  ErrorLog(237, e.Message);
27376  }
27377 }
27378 
27379 //---------------------------------------------------------------------------
27380 
27381 void __fastcall TInterface::MPHPOwnIPEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
27382 
27383 {
27384  try
27385  {
27386  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPHPOwnIPEditBoxKeyUp," + AnsiString(Key));
27387  if(Key == '\x0D') //CR
27388  {
27390  {
27391  ShowMessage("Format should be 'a.b.c.d' where a, b, c, and d are numbers between 0 and 255.\n\nObtain the address from the host and enter exactly as given.");
27392  MPHPOwnIPEditBox->Text = "";
27393  Utilities->CallLogPop(2351);
27394  return;
27395  }
27396  MPHPOwnIPEditBox->Enabled = false;
27397  if(MPHPOwnPortEditBox->Enabled == false)
27398  {
27399  MPHostClient->BoundIP = MPHPOwnIPEditBox->Text;
27400  MPHostClient->BoundPort = (unsigned short)MPHPOwnPortEditBox->Text.ToInt();
27401  MPHPLoadCouplingFileButton->Enabled = true;
27402  MPHPGeneralLabel->Caption = "Load coupling file";
27403  }
27404  }
27405  else
27406  {
27407  for(int x = 1; x <= MPHPOwnIPEditBox->Text.Length(); x++)
27408  {
27409  if((MPHPOwnIPEditBox->Text[x] != '.') && ((MPHPOwnIPEditBox->Text[x] < '0') || (MPHPOwnIPEditBox->Text[x] > '9')))
27410  {
27411  ShowMessage("Format should be 'a.b.c.d' where a, b, c, and d are numbers between 0 and 255.\n\nObtain the address from the host and enter exactly as given.");
27412  MPHPOwnIPEditBox->Text = "";
27413  Utilities->CallLogPop(2352);
27414  return;
27415  }
27416  }
27417  }
27418  Utilities->CallLogPop(2353);
27419  }
27420  catch(const EIdException &e) //non-error catch
27421  {
27422  ShowMessage("MPHPOwnIPEditBoxKeyUp " + e.Message); //<--temporary
27423  Utilities->CallLogPop(2411);
27424  }
27425  catch(const Exception &e)
27426  {
27427  ErrorLog(228, e.Message);
27428  }
27429 }
27430 //---------------------------------------------------------------------------
27431 
27432 void __fastcall TInterface::MPHPOwnPortEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
27433 
27434 {
27435  try
27436  {
27437  TrainController->LogEvent("MPHPOwnPortEditBoxKeyUp," + AnsiString(Key));
27438  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPHPOwnPortEditBoxKeyUp," + AnsiString(Key));
27439  if(Key == '\x0D') //CR
27440  {
27441  if((MPHPOwnPortEditBox->Text == "") || (MPHPOwnPortEditBox->Text.Length() < 5) || (MPHPOwnPortEditBox->Text.ToInt() < 49152) || (MPHPOwnPortEditBox->Text.ToInt() > 59999))
27442  {
27443  ShowMessage("Entry should be a number between 49152 and 59999.");
27444  MPHPOwnPortEditBox->Text = "";
27445  Utilities->CallLogPop(2354);
27446  return;
27447  }
27448  MPHPOwnPortEditBox->Enabled = false;
27449  if(MPHPOwnIPEditBox->Enabled == false)
27450  {
27451  MPHostClient->BoundIP = MPHPOwnIPEditBox->Text;
27452  MPHostClient->BoundPort = (unsigned short)MPHPOwnPortEditBox->Text.ToInt();
27453  MPHPLoadCouplingFileButton->Enabled = true;
27454  MPHPGeneralLabel->Caption = "Load coupling file";
27455  }
27456  }
27457  else
27458  {
27459  for(int x = 1; x <= MPHPOwnPortEditBox->Text.Length(); x++)
27460  {
27461  if((MPHPOwnPortEditBox->Text[x] < '0') || (MPHPOwnPortEditBox->Text[x] > '9'))
27462  {
27463  ShowMessage("Entry should be a number between 49152 and 59999.");
27464  MPHPOwnPortEditBox->Text = "";
27465  Utilities->CallLogPop(2355);
27466  return;
27467  }
27468  }
27469  }
27470  Utilities->CallLogPop(2356);
27471  }
27472  catch(const EIdException &e) //non-error catch
27473  {
27474  ShowMessage("MPHPOwnPortEditBoxKeyUp " + e.Message); // <--temporary
27475  Utilities->CallLogPop(2412);
27476  }
27477  catch(const Exception &e)
27478  {
27479  ErrorLog(229, e.Message);
27480  }
27481 }
27482 
27483 //---------------------------------------------------------------------------
27484 
27485 void __fastcall TInterface::IPCheckLinkLabelLinkClick(TObject *Sender, const UnicodeString Link,
27486  TSysLinkType LinkType)
27487 {
27488  if(LinkType == sltURL)
27489  {
27490  ::ShellExecute(Handle, NULL, Link.c_str(), NULL, NULL, SW_SHOWNORMAL);
27491  }
27492 }
27493 
27494 //---------------------------------------------------------------------------
27495 
27497 {
27498  if(lower.first < higher.first) //RailwayName
27499  {
27500  return(true);
27501  }
27502  else if(lower.first > higher.first)
27503  {
27504  return(false);
27505  }
27506  else if(lower.second.first < higher.second.first) //railway names same, lower H < higher H
27507  {
27508  return(true);
27509  }
27510  else if(lower.second.first > higher.second.first) //railway names same, lower H > Higher H
27511  {
27512  return(false);
27513  }
27514  else if(lower.second.second < higher.second.second) //railway names same, H values same, lower V < higher V
27515  {
27516  return(true);
27517  }
27518  else return(false); //railway names same, H values same, lower V > higher V or both V values same
27519  //both H & V values shouldn't be same for same railway but if for some reason they are then
27520  //the map will reject one of them as both comp(a, b) and comp(b, a) are false
27521 }
27522 
27523 // ---------------------------------------------------------------------------
27524 
27525 bool TInterface::TDynamicMapComp:: operator()(const TNumHVPair& lower, const TNumHVPair& higher) const
27526 {
27527  if(lower.first < higher.first) //compare numbers
27528  {
27529  return(true);
27530  }
27531  else if(lower.first > higher.first)
27532  {
27533  return(false);
27534  }
27535  else if(lower.second.first < higher.second.first) //number values same (same railway/user), compare H values
27536  {
27537  return(true);
27538  }
27539  else if(lower.second.first > higher.second.first)
27540  {
27541  return(false);
27542  }
27543  else if(lower.second.second < higher.second.second) //number & H values same, compare V values
27544  {
27545  return(true);
27546  }
27547  else if(lower.second.second > higher.second.second)
27548  {
27549  return(false);
27550  }
27551  else return(false); //number + H & V values same, shouldn't be same for same railway but if for some reason they are then
27552  //the map will reject one of them as both comp(a, b) and comp(b, a) are false
27553 }
27554 
27555 // ---------------------------------------------------------------------------
27556 
27557 bool TInterface::PopulateCouplingMap(AnsiString FileName, int &NumExt)
27558 // read file CouplingMap.csv (in same directory as railway.exe) and build AnsiCouplingMap (based on railway name), AllRailwaysCouplingMap (based on
27559 //RlyUserNumber) & HostCombinedDynamicMap. The maps have two sets of couplings so each railway/HVpair is a key
27560 {
27561  TrainController->LogEvent("PopulateCouplingMap");
27562  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PopulateCouplingMap,FileName," + FileName); //no caller as only called once
27563  AnsiString RailwayName1 = "", RailwayName2 = "", OneLine = "", ExitID1 = "", ExitID2 = "";
27564  int HLoc1, HLoc2, VLoc1, VLoc2, CPos, NumRailways;
27565  TAnsiCouplingPair AnsiCouplingPair1, AnsiCouplingPair2;
27566  TAnsiCouplingMapEntry AnsiCouplingMapEntry;
27567  AnsiCouplingMap.clear();
27568  THVShortPair HVExitPair1, HVExitPair2;
27569  std::ifstream TxtFile(FileName.c_str()); //railway1, HLoc, VLoc, railway2, HLoc, VLoc CRLF repeated
27570  AnsiString SyntaxError = "Coupling file error - structure should consist of lines of 'R1; exit1ID; R2; exit2ID' and so on "
27571  "for all coupled exits, where R1 and R2 are railway names without the '.rly' extension, exit1ID is an "
27572  "exit ID in R1, and exit2ID is an exit ID in R2";
27573  if(TxtFile.fail())
27574  {
27575  ShowMessage("Can't find the coupling file. Please make sure it's located in the directory selected and has a .txt extension");
27576  Utilities->CallLogPop(2325);
27577  return(false);
27578  }
27579  while(!TxtFile.eof())
27580  {
27581  if(!Utilities->ReadOneLineFromCouplingFile(TxtFile, OneLine))
27582  {
27583  ShowMessage(SyntaxError);
27584  TxtFile.close();
27585  Utilities->CallLogPop(2326);
27586  return(false);
27587  }
27588  if(TxtFile.eof() && (OneLine == ""))
27589  {
27590  break;
27591  }
27592  if(OneLine.AnsiPos(',') != 0)
27593  {
27594  ShowMessage(SyntaxError);
27595  TxtFile.close();
27596  Utilities->CallLogPop(2327);
27597  return(false);
27598  }
27599  TrainController->StripSpaces(6, OneLine);
27600  // strip both leading and trailing spaces at ends of line and spaces before and after all commas (and semicolons) within the line
27601  if(OneLine != "")
27602  {
27603  int DelimPos = OneLine.Pos(';');
27604  if(DelimPos < 2) //0 = can't find, 1 = first character is a comma - both errors
27605  {
27606  ShowMessage(SyntaxError);
27607  TxtFile.close();
27608  Utilities->CallLogPop(2328);
27609  return(false);
27610  }
27611  else
27612  {
27613  RailwayName1 = OneLine.SubString(1, (DelimPos - 1));
27614  OneLine = OneLine.SubString(DelimPos + 1, OneLine.Length() - DelimPos); //OneLine is now the remainder
27615  if(RailwayName1.LowerCase().SubString(RailwayName1.Length() - 3, 4) == ".rly")
27616  {
27617  RailwayName1 = RailwayName1.SubString(1, RailwayName1.Length() - 4); //ignore .rly if present
27618  }
27619  }
27620  DelimPos = OneLine.Pos(';');
27621  if(DelimPos < 2) //0 = can't find, 1 = first character is a comma - both errors
27622  {
27623  ShowMessage(SyntaxError);
27624  TxtFile.close();
27625  Utilities->CallLogPop(2329);
27626  return(false);
27627  }
27628  else
27629  {
27630  ExitID1 = OneLine.SubString(1, (DelimPos - 1));
27631  OneLine = OneLine.SubString(DelimPos + 1, OneLine.Length() - DelimPos); //OneLine is now the remainder
27632  if(!ConvertIDToPair(ExitID1, HVExitPair1))
27633  {
27634  TxtFile.close();
27635  Utilities->CallLogPop(2330);
27636  return(false);
27637  }
27638  }
27639  DelimPos = OneLine.Pos(';');
27640  if(DelimPos < 2) //0 = can't find, 1 = first character is a comma - both errors
27641  {
27642  ShowMessage(SyntaxError);
27643  TxtFile.close();
27644  Utilities->CallLogPop(2331);
27645  return(false);
27646  }
27647  else
27648  {
27649  RailwayName2 = OneLine.SubString(1, (DelimPos - 1));
27650  OneLine = OneLine.SubString(DelimPos + 1, OneLine.Length() - DelimPos); //OneLine is now the remainder
27651  if(RailwayName2.LowerCase().SubString(RailwayName2.Length() - 3, 4) == ".rly")
27652  {
27653  RailwayName2 = RailwayName2.SubString(1, RailwayName2.Length() - 4); //ignore .rly if present
27654  }
27655  }
27656  if(OneLine == "") //should now only contain Exit2ID
27657  {
27658  ShowMessage(SyntaxError);
27659  TxtFile.close();
27660  Utilities->CallLogPop(2332);
27661  return(false);
27662  }
27663  else
27664  {
27665  ExitID2 = OneLine;
27666  if(!ConvertIDToPair(ExitID2, HVExitPair2))
27667  {
27668  TxtFile.close();
27669  Utilities->CallLogPop(2333);
27670  return(false);
27671  }
27672  }
27673  AnsiCouplingPair1.first = RailwayName1;
27674  AnsiCouplingPair1.second = HVExitPair1;
27675  AnsiCouplingPair2.first = RailwayName2;
27676  AnsiCouplingPair2.second = HVExitPair2;
27677  AnsiCouplingMapEntry.first = AnsiCouplingPair1;
27678  AnsiCouplingMapEntry.second = AnsiCouplingPair2;
27679  AnsiCouplingMap.insert(AnsiCouplingMapEntry);
27680  AnsiCouplingMapEntry.first = AnsiCouplingPair2; //add entry for reversed pairs
27681  AnsiCouplingMapEntry.second = AnsiCouplingPair1;
27682  AnsiCouplingMap.insert(AnsiCouplingMapEntry);
27683  RailwayList.push_back(RailwayName1);
27684  RailwayList.push_back(RailwayName2);
27685  }
27686  else
27687  {
27688  continue; //ignore blank lines
27689  }
27690  }
27691  TxtFile.close();
27692  RailwayList.sort();
27693  RailwayList.unique(); //remove duplicates
27694  NumPlayers = RailwayList.size();
27695 //now convert to number based coupling map
27696  AllRailwaysCouplingMap.clear();
27697  TNumHVPair NumHVPair1, NumHVPair2;
27698  for(TAnsiCouplingMap::iterator ACMIt = AnsiCouplingMap.begin(); ACMIt != AnsiCouplingMap.end(); ACMIt++)
27699  {
27700  int ListCounter = 1; //RlyUserNumbers start at 1
27701  for(RLIt = RailwayList.begin(); RLIt != RailwayList.end(); RLIt++)
27702  {
27703  if(ACMIt->first.first == *RLIt)
27704  {
27705  NumHVPair1.first = ListCounter;
27706  NumHVPair1.second = ACMIt->first.second;
27707  break;
27708  }
27709  ListCounter++;
27710  }
27711  ListCounter = 1;
27712  for(RLIt = RailwayList.begin(); RLIt != RailwayList.end(); RLIt++)
27713  {
27714  if(ACMIt->second.first == *RLIt)
27715  {
27716  NumHVPair2.first = ListCounter;
27717  NumHVPair2.second = ACMIt->second.second;
27718  break;
27719  }
27720  ListCounter++;
27721  }
27722  AllRailwaysCouplingPair.first = NumHVPair1;
27723  AllRailwaysCouplingPair.second = NumHVPair2;
27724  AllRailwaysCouplingMap.insert(AllRailwaysCouplingPair); //includes reversed couplings
27725  }
27726 //now build the HostCombinedDynamicMap without any service info
27727  HostCombinedDynamicMap.clear();
27728  TDynamicMapEntry DMEntry;
27729  TServiceInfo ServiceInfo; //default values
27730  for(TCMIterator CMIt = AllRailwaysCouplingMap.begin(); CMIt != AllRailwaysCouplingMap.end(); CMIt++)
27731  {
27732  DMEntry.first = CMIt->first;
27733  DMEntry.second = ServiceInfo;
27734  HostCombinedDynamicMap.insert(DMEntry); //includes reversed couplings
27735  }
27736  ShowMessage("File loaded successfully");
27737  Utilities->CallLogPop(2334);
27738  return(true);
27739 }
27740 
27741 // ---------------------------------------------------------------------------
27742 
27743 bool TInterface::ConvertIDToPair(AnsiString HVID, THVShortPair &HVPair) //true for ok
27744 {
27745  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ConvertIDToPair," + HVID); //no caller as only called twice & HVID identifies
27746  AnsiString HString = "", VString = "";
27747  if(HVID == "") //should be covered in calling function but include anyway
27748  {
27749  ShowMessage("Coupling file error - an exit ID is missing from the file");
27750  Utilities->CallLogPop(2335);
27751  return(false);
27752  }
27753  for(int x = 1; x <= HVID.Length(); x++)
27754  {
27755  char C = HVID[x];
27756  if(((C < '0') || (C > '9')) && (C != 'N') && (C != '-'))
27757  {
27758  ShowMessage("Coupling file error - an exit ID contains illegal characters, can only include digits, '-' or 'N'");
27759  Utilities->CallLogPop(2336);
27760  return(false);
27761  }
27762  }
27763  int DashPos = HVID.Pos('-');
27764  if(DashPos == 0)
27765  {
27766  ShowMessage("Coupling file error - structure should consist of lines of 'R1, exit1 ID, R2, corresponding exit ID' and so on "
27767  "for all coupled exits, where R1 and R2 are railway names without the '.rly' extension");
27768  Utilities->CallLogPop(2337);
27769  return(false);
27770  }
27771  else
27772  {
27773  HString = HVID.SubString(1, DashPos - 1);
27774  VString = HVID.SubString(DashPos + 1, HVID.Length() - DashPos);
27775  if(HString[1] == 'N')
27776  {
27777  HString = HString.SubString(2, HString.Length() - 1);
27778  HVPair.first = -HString.ToInt();
27779  }
27780  else
27781  {
27782  HVPair.first = HString.ToInt();
27783  }
27784  if(VString[1] == 'N')
27785  {
27786  VString = VString.SubString(2, VString.Length() - 1);
27787  HVPair.second = -VString.ToInt();
27788  }
27789  else
27790  {
27791  HVPair.second = VString.ToInt();
27792  }
27793  }
27794  Utilities->CallLogPop(2338);
27795  return(true);
27796 }
27797 
27798 // ---------------------------------------------------------------------------
27799 
27800 bool TInterface::MultiplayerRailwayValid(AnsiString RailwayName, char &ErrorNumber)
27801 {
27802  Utilities->CallLog.push_back(Utilities->TimeStamp() + "MultiplayerRailwayValid");
27803  ErrorNumber = 0; //no error
27804  bool Listed = false, Available = false, RetType = false;
27805  for(int x = 2; x < NumPlayers + 2; x++)
27806  {
27807  if(RailwayName == MultiplayerHostStringGrid->Cells[1][x])
27808  {
27809  Listed = true;
27810  if(MultiplayerHostStringGrid->Cells[0][x] == "")
27811  {
27812  Available = true;
27813  }
27814  break;
27815  }
27816  }
27817  if(!Listed)
27818  {
27819  ErrorNumber = 1;
27820  }
27821  else if(!Available)
27822  {
27823  ErrorNumber = 2;
27824  }
27825  else
27826  {
27827  RetType = true;
27828  }
27829  Utilities->CallLogPop(2357);
27830  return(RetType);
27831 }
27832 
27833 //---------------------------------------------------------------------------
27834 
27835 short TInterface::BuildOneRailwayCouplingMap(unsigned char PlayerNumber)
27836 {
27837  Utilities->CallLog.push_back(Utilities->TimeStamp() + "BuildOneRailwayCouplingMap");
27838  OneRailwayCouplingMap.clear();
27839  TCouplingMap::iterator CMIt;
27840  short Count = 0;
27841  for(CMIt = AllRailwaysCouplingMap.begin(); CMIt != AllRailwaysCouplingMap.end(); CMIt++)
27842  {
27843  if(CMIt->first.first == PlayerNumber)
27844  {
27845  OneRailwayCouplingPair.first = CMIt->first; //own player number + H & V for own railway
27846  OneRailwayCouplingPair.second = CMIt->second; //other player number & coupled H&V
27848  Count++;
27849  }
27850  }
27851  Utilities->CallLogPop(2378);
27852  return(Count * 10); //this is the number of bytes to be entered into a datagram (apart from marker & username if they are required)
27853 }
27854 
27855 //---------------------------------------------------------------------------
27856 
27858 { //host read & respond new for multiplayer - every cycle
27859  try
27860  {
27861  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",HostHandshakingActions");
27863  {
27864  EIdExceptionSource += " CouplingFileLoadedAwaitingPlayers";
27865  UnicodeString Data = "";
27866  UnicodeString PeerIP = "";
27867  unsigned short PeerPort = 0;
27868  unsigned char marker;
27869  bool Found1 = false, Found2 = false;
27870  AnsiString PlayerRailwayName, PlayerUserName;
27871  char ErrorNumber; //indicates type of error 0 = ok, 1 = not listed, 2 = already taken
27872  UnicodeString Message1 = "The railway doesn't appear in the coupled group - the host can advise which railways are valid";
27873  UnicodeString Message2 = "The railway has already been allocated - the host can advise which are still available";
27874  TBytes HostBuffer;
27875  HostBuffer.Length = 8192; //have to set long enough initially, will reduce later
27876  HostBuffer.Length = MPHostClient->ReceiveBuffer(HostBuffer, PeerIP, PeerPort, 10);
27877  //will be of form 'marker' then UserName ';' RailwayName (marker is '1' for first contact, '2' for ready to begin, no space or delimieter before railway name)
27878  MPHostClient->Active = false; //don't receive any messages until have dealt with this
27879  if(HostBuffer.Length != 0)
27880  {
27881  if(HostBuffer[0] == '1') //player first contact, no binary content
27882  {
27883  //check validity
27884  bool FoundFlag = false;
27885  for(int x = 1; x < HostBuffer.Length; x++)
27886  {
27887  if((HostBuffer[x] == ';') && (x > 1) && (x < (HostBuffer.Length - 1))) // ';' present and not first ot last character
27888  {
27889  FoundFlag = true;
27890  break;
27891  }
27892  }
27893  if(!FoundFlag)
27894  {
27895  Utilities->CallLogPop(2387); //skip this loop
27896  return;
27897  }
27898  for(int x = 1; x < HostBuffer.Length; x++)
27899  {
27900  if((HostBuffer[x] < ' ') || (HostBuffer[x] > '~'))
27901  {
27902  MPHostClient->Active = true;
27903  Utilities->CallLogPop(2371); //skip this loop
27904  return;
27905  }
27906  }
27907  Data = BytesToString(HostBuffer);
27908  int DelimPos = Data.Pos(';');
27909  PlayerUserName = Data.SubString(2, DelimPos - 2);
27910  PlayerRailwayName = Data.SubString(DelimPos + 1, Data.Length() - DelimPos);
27911  unsigned char PlayerNumber;
27912  if(!RlyToNum(PlayerRailwayName, PlayerNumber))
27913  {
27914  ShowMessage("Error in converting incoming information for player '" + PlayerUserName + "', ask player to cancel and join again");
27915  RemovePlayerFromStringGridAndInfoVector(0, PlayerUserName);
27916  MPHostClient->Active = true;
27917  Utilities->CallLogPop(2372); //skip this loop
27918  return;
27919  }
27920  if(MultiplayerRailwayValid(PlayerRailwayName, ErrorNumber))
27921  {
27922  //host can accept or reject
27923  UnicodeString RejectString = PlayerUserName + " is asking to join with railway " + PlayerRailwayName + ". OK to join?";
27924  int button = Application->MessageBox(RejectString.c_str(), L"Request", MB_YESNO);
27925  if(button == IDNO)
27926  {
27927  MPHostClient->SendBuffer(PeerIP, PeerPort, ToBytes("The host did not accept your request to join"));
27928  MPHostClient->Active = false; //keep it false (send sets it to true)
27929  }
27930  else
27931  {
27932  //find correct slot in MultiplayerHostStringGrid & InfoVector & add username
27933  for(int x = 0; x < NumPlayers; x++)
27934  {
27935  if(MultiplayerHostStringGrid->Cells[1][x + 2] == PlayerRailwayName)
27936  {
27937  MultiplayerHostStringGrid->Cells[0][x + 2] = PlayerUserName;
27938  Found1 = true;
27939  break;
27940  }
27941  }
27942  for(int x = 0; x < NumPlayers; x++)
27943  {
27944  if(InfoVector.at(x).RailwayName == PlayerRailwayName) //already have number
27945  {
27946  InfoVector.at(x).UserName = PlayerUserName;
27947  InfoVector.at(x).UserIP = PeerIP;
27948  InfoVector.at(x).UserPort = PeerPort;
27949  Found2 = true;
27950  break;
27951  }
27952  }
27953  if(!Found1 || !Found2)
27954  {
27955  //error, should find both
27956  ShowMessage("Error in initial contact information for player '" + PlayerUserName + "', ask player to cancel and join again");
27957  RemovePlayerFromStringGridAndInfoVector(1, PlayerUserName);
27958  MPHostClient->Active = true;
27959  Utilities->CallLogPop(2373); //skip this loop
27960  return;
27961  }
27962 // now send OwnRlyUserNumber + shortHVs then coupled number + short HVs (10 bytes total per exit/entry), no separators
27963  short NumBytes = BuildOneRailwayCouplingMap(PlayerNumber);
27964  TCouplingMap::iterator CMIt = OneRailwayCouplingMap.begin();
27965  TBytes HVbuffer;
27966  HVbuffer.Length = NumBytes;
27967  for(int x = 0; x < NumBytes; x += 10)
27968  {
27969  HVbuffer[x] = CMIt->first.first; // = player's OwnRlyUserNumber
27970  HVbuffer[x + 1] = CMIt->first.second.first & 0x00FF; //H low then high
27971  HVbuffer[x + 2] = (CMIt->first.second.first & 0xFF00) / 256;
27972  HVbuffer[x + 3] = CMIt->first.second.second & 0x00FF; //V low then high
27973  HVbuffer[x + 4] = (CMIt->first.second.second & 0xFF00) / 256;
27974 
27975  HVbuffer[x + 5] = CMIt->second.first; //coupled railway usernumber
27976  HVbuffer[x + 6] = CMIt->second.second.first & 0x00FF; //H low then high
27977  HVbuffer[x + 7] = (CMIt->second.second.first & 0xFF00) / 256;
27978  HVbuffer[x + 8] = CMIt->second.second.second & 0x00FF; //V low then high
27979  HVbuffer[x + 9] = (CMIt->second.second.second & 0xFF00) / 256;
27980  CMIt++;
27981  }
27982  MPHostClient->SendBuffer(PeerIP, PeerPort, HVbuffer);
27983  MPHostClient->Active = false; //keep it false
27984  }
27985  }
27986  else
27987  {
27988  //send message that not listed (1) or already taken (2) - cancel panel and load a valid railway (ask host which ok)
27989  //send it outside the data receipt as data won't keep being sent from player
27990  if(ErrorNumber == 1)
27991  {
27992  MPHostClient->SendBuffer(PeerIP, PeerPort, ToBytes(Message1));
27993  }
27994  else if(ErrorNumber == 2)
27995  {
27996  MPHostClient->SendBuffer(PeerIP, PeerPort, ToBytes(Message2));
27997  }
27998  MPHostClient->Active = false; //keep it false (send sets it to true)
27999  }
28000  }
28001  else if(HostBuffer[0] == '2') //player second contact, marker = 2, username, then DynMapToHost in buffer form
28002  {
28003  TDynamicMap::iterator DMIt, HMIt;
28004  if(BuildDynamicMapFromPlayerDatagram(0, DynMapToHost, HostBuffer, marker, PlayerUserName)) //this performs validity checks
28005  {
28006 // update HostCombinedDynamicMap. Won't alter anything here as no service information, but acts as a check that can find all elements
28007  for(DMIt = DynMapToHost.begin(); DMIt != DynMapToHost.end(); DMIt++) //unlikely to be in alphabetical order
28008  {
28009  HMIt = HostCombinedDynamicMap.find(DMIt->first); //should always find it
28010  if(HMIt != HostCombinedDynamicMap.end())
28011  {
28012  HMIt->second = DMIt->second;
28013  }
28014  else
28015  {
28016  ShowMessage("Failed to locate player information in host database for player '" + PlayerUserName + "', ask player to cancel and join again");
28017  RemovePlayerFromStringGridAndInfoVector(2, PlayerUserName);
28018  Utilities->CallLogPop(2374); //skip this loop
28019  return;
28020  }
28021  }
28022  }
28023  else
28024  {
28025  Utilities->CallLogPop(2375); //skip this loop
28026  return;
28027  }
28028  //update StringGrid & InfoVector
28029  Found1 = false;
28030  Found2 = false;
28031  for(int x = 0; x < NumPlayers; x++)
28032  {
28033  if(MultiplayerHostStringGrid->Cells[0][x + 2] == PlayerUserName)
28034  {
28035  MultiplayerHostStringGrid->Cells[2][x + 2] = "Yes";
28036  Found1 = true;
28037  }
28038  }
28039  for(int x = 0; x < NumPlayers; x++)
28040  {
28041  if(InfoVector.at(x).UserName == PlayerUserName) //already have number
28042  {
28043  InfoVector.at(x).UserIP = PeerIP;
28044  InfoVector.at(x).UserPort = PeerPort;
28045  Found2 = true;
28046  }
28047  }
28048  if(!Found1 || !Found2)
28049  {
28050  //error, should find both
28051  ShowMessage("Error in 'ready to begin' contact information for player '" + PlayerUserName + "', ask player to cancel and join again");
28052  RemovePlayerFromStringGridAndInfoVector(3, PlayerUserName);
28053  Utilities->CallLogPop(2388); //skip this loop
28054  return;
28055  }
28056 // now send return message to player
28057  TBytes HVbuffer;
28058  HVbuffer.Length = 22;
28059  UnicodeString Message = "Await simulation start";
28060  HVbuffer = ToBytes(Message);
28061  MPHostClient->SendBuffer(PeerIP, PeerPort, HVbuffer);
28062  MPHostClient->Active = false; //keep it false (send sets it to true)
28063 //check if all players ready to start, and if so enable the 'start session' button
28064  bool AllReady = true;
28065  for(int x = 0; x < NumPlayers; x++)
28066  {
28067  if(MultiplayerHostStringGrid->Cells[2][x + 2] != UnicodeString("Yes"))
28068  {
28069  AllReady = false;
28070  }
28071  }
28072  if(AllReady)
28073  {
28074  MPHPStartButton->Enabled = true;
28075  }
28076  }
28077  else if(HostBuffer[0] == '3') //player clicked cancel button
28078  {
28079  for(int x = 1; x < HostBuffer.Length; x++) //validity check
28080  {
28081  if((HostBuffer[x] < ' ') || (HostBuffer[x] > '~'))
28082  {
28083  Utilities->CallLogPop(2392); //skip this loop
28084  return;
28085  }
28086  }
28087  Data = BytesToString(HostBuffer); //message is '3' then username and nothing else
28088  PlayerUserName = Data.SubString(2, Data.Length() - 1);
28089  TBytes HVbuffer;
28090  HVbuffer.Length = 9;
28091  UnicodeString Message = "Cancelled";
28092  HVbuffer = ToBytes(Message);
28093  MPHostClient->SendBuffer(PeerIP, PeerPort, HVbuffer); //send response each time received but only show message etc. once
28094  MPHostClient->Active = false; //keep it false (send sets it to true)
28095  unsigned char RlyUserNumber;
28096  if(!UserToNum(PlayerUserName, RlyUserNumber)) //use this to check if already removed from vector
28097  {
28098  Utilities->CallLogPop(2391); //already removed from infovector so go no further
28099  return;
28100  }
28101  else
28102  {
28103  ShowMessage("Player '" + PlayerUserName + "', has cancelled the join request");
28104  RemovePlayerFromStringGridAndInfoVector(4, PlayerUserName);
28105  Utilities->CallLogPop(2389);
28106  return;
28107  }
28108  }
28109  else if(HostBuffer[0] == '4') //player awaiting start
28110  {
28111  if(HostInSessionFlag)
28112  {
28113  for(int x = 1; x < HostBuffer.Length; x++) //validity check
28114  {
28115  if((HostBuffer[x] < ' ') || (HostBuffer[x] > '~'))
28116  {
28117  Utilities->CallLogPop(2393); //skip this loop
28118  return;
28119  }
28120  }
28121  Data = BytesToString(HostBuffer); //message is '3' then username and nothing else
28122  PlayerUserName = Data.SubString(2, Data.Length() - 1);
28123  TBytes HVbuffer;
28124  HVbuffer.Length = 13;
28125  UnicodeString Message = "Start Session";
28126  HVbuffer = ToBytes(Message);
28127  MPHostClient->SendBuffer(PeerIP, PeerPort, HVbuffer); //send response each time received, eventually all players will start
28128  //when players are in session they just message every 5 secs & get TTClock in response
28129  MPHostClient->Active = false; //keep it false (send sets it to true)
28130  }
28131  //else ignore
28132  }
28133  else if(HostBuffer[0] == '5') //in session
28134  {
28135  TDynamicMap::iterator DMIt, HMIt;
28136  if(BuildDynamicMapFromPlayerDatagram(1, DynMapToHost, HostBuffer, marker, PlayerUserName)) //this performs validity checks
28137  {
28138 // update HostCombinedDynamicMap
28139  for(DMIt = DynMapToHost.begin(); DMIt != DynMapToHost.end(); DMIt++) //unlikely to be in alphabetical order
28140  {
28141  HMIt = HostCombinedDynamicMap.find(DMIt->first); //should always find it
28142  if(HMIt != HostCombinedDynamicMap.end()) //if not skip
28143  {
28144  HMIt->second = DMIt->second;
28145  }
28146  }
28147  }
28148  //construct DynMapFromHost & send as buffer along with TTClockTime, then at player end receive it and update TTClock etc
28149  unsigned char RlyUserNumber;
28150  if(!UserToNum(PlayerUserName, RlyUserNumber)) //get number from InfoVector
28151  {
28152  Utilities->CallLogPop(2395); //skip this loop
28153  return;
28154  }
28155  DynMapFromHost.clear();
28156  bool LoadingFlag = false;
28157  HMIt = HostCombinedDynamicMap.begin();
28158  while(HMIt != HostCombinedDynamicMap.end())
28159  {
28160  if(HMIt->first.first == RlyUserNumber)
28161  {
28162  DynMapFromHost.insert(*HMIt);
28163  LoadingFlag = true;
28164  }
28165  if(LoadingFlag && (HMIt->first.first != RlyUserNumber))
28166  {
28167  break; //no point looking after last insert
28168  }
28169  HMIt++;
28170  }
28171  TBytes HVbuffer;
28172  HVbuffer.Length = 8192;
28173  BuildDatagramFromHostMap(0, HVbuffer, DynMapFromHost); //length set at end of this function, this includes TTClockTime as 32 bit integer
28174  MPHostClient->SendBuffer(PeerIP, PeerPort, HVbuffer);
28175  MPHostClient->Active = false; //keep it false (send sets it to true)
28176  }
28177 
28178  //if hostbuffer[0] outside accepted range ignore
28179  }
28180  else
28181  {
28182  //nothing to read or error in datagram format - if error ignore & read next time it's sent
28183  }
28184  }
28185  MPHostClient->Active = true;
28186  Utilities->CallLogPop(2415);
28187  }
28188  catch(const EIdException &e) //non-error catch
28189 //if no response from peer then get a 'connection reset by peer' message which isn't valid
28190  {
28191  Utilities->CallLogPop(2414);
28192  }
28193  catch(const Exception &e)
28194  {
28195  ErrorLog(239, e.Message);
28196  }
28197 }
28198 
28199 //---------------------------------------------------------------------------
28200 //player functions
28201 //---------------------------------------------------------------------------
28202 
28203 
28204 void __fastcall TInterface::JoinMultiplayerSessionMenuItemClick(TObject *Sender) //player first action
28205 {
28206  try
28207  {
28208  TrainController->LogEvent("JoinMultiplayerSessionMenuItemClick");
28209  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",JoinMultiplayerSessionMenuItemClick");
28210  MultiplayerPlayerPanel->Visible = true;
28211  MultiplayerPlayerPanel->Left = MainScreen->Left + (MainScreen->Width / 2) - (MultiplayerPlayerPanel->Width / 2);
28212  MultiplayerPlayerPanel->Top = MainScreen->Top + 100;
28213  MPPlayerClient->Active = false;
28214  MPCPLabel2->Caption = "Complete the boxes and press 'Enter' for each."; //top box
28215  MPCPLabel4->Caption = "Joining request not yet sent"; //opp image
28216  MPCPLabel7->Caption = "Only click 'Send' when the host is ready - the host will"; //lower box top
28217  MPCPLabel8->Caption = "advise by communicating outside the game."; //lower box bottom
28218  MPCPLabel5->Caption = "Host IP address (obtain from host)";
28219  MPCPLabel6->Caption = "Host port number (obtain from host)";
28220  MPCPLabel3->Caption = "Player name (2 to 4 characters)";
28221 
28222  MultiplayerHostSessionMenuItem->Enabled = false;
28223  SaveMultiplayerSessionMenuItem->Enabled = false;
28224  EndSimulationMenuItem->Enabled = false;
28225  ShowHideStringGridMenuItem->Enabled = false;
28226  JoinMultiplayerSessionMenuItem->Enabled = false;
28227  ExitSimulationMenuItem->Enabled = false;
28228  HostInSessionFlag = false;
28229  CouplingFileLoadedFlag = false;
28231  PlayerReadyToBeginFlag = false;
28232  PlayerCancelJoinFlag = false;
28234  PlayerInSessionFlag = false;
28235 
28236  MPCPHostIPEditBox->Visible = true;
28237  MPCPHostPortEditBox->Visible = true;
28238  MPCPPlayerNameEditBox->Visible = true;
28239  MPCPHostImage->Visible = true;
28240  MPCPHostImage->Picture->Assign(RailGraphics->SolidCircleRed);
28241  MPCPSendButton->Enabled = false;
28242  MPCPReadyToBeginButton->Enabled = false;
28243  MPCPPlayerNameEditBox->Enabled = true;
28244  MPCPHostPortEditBox->Enabled = true;
28245  MPCPHostIPEditBox->Enabled = true;
28246  MPCPPlayerNameEditBox->Text = "AB"; // <--temporary - change to "" for release
28247  MPCPHostPortEditBox->Text = "50000";// <--temporary - change to "" for release
28248  MPCPHostIPEditBox->Text = "127.0.0.1";// <--temporary - change to "" for release
28249  Utilities->CallLogPop(2358);
28250  }
28251 
28252  catch(const Exception &e)
28253  {
28254  ErrorLog(230, e.Message);
28255  }
28256 }
28257 
28258 //---------------------------------------------------------------------------
28259 
28260 void __fastcall TInterface::MPCPSendButtonClick(TObject *Sender)
28261 {
28262  try
28263  {
28264  TrainController->LogEvent("MPCPSendButtonClick");
28265  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPSendButtonClick");
28266  MPCPLabel2->Caption = "Await host response then click 'Ready to begin'";
28267  MPCPLabel4->Caption = "Awaiting host response";
28268  MPCPLabel7->Caption = "";
28269  MPCPLabel8->Caption = "";
28270  MPCPHostImage->Picture->Assign(RailGraphics->SolidCircleYellow);
28271  MPCPSendButton->Enabled = false;
28273  P5SCounter = 0; //temporary
28274 
28275 
28276 //send message every 10 seconds and listen for response
28277 //when response received give 'received message', give greem light, and 'click ready to bein' message
28278 
28279 
28280  Utilities->CallLogPop(2359);
28281  }
28282 
28283  catch(const Exception &e)
28284  {
28285  ErrorLog(231, e.Message);
28286  }
28287 }
28288 
28289 //---------------------------------------------------------------------------
28290 
28291 void __fastcall TInterface::MPCPCancelButtonClick(TObject *Sender)
28292 {
28293  try
28294  {
28295  TrainController->LogEvent("MPCPCancelButtonClick");
28296  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPCancelButtonClick");
28297  MPCPSendButton->Enabled = false;
28298  MPCPReadyToBeginButton->Enabled = false;
28299  MPCPPlayerNameEditBox->Text = "";
28300  MPCPHostPortEditBox->Text = "";
28301  MPCPHostIPEditBox->Text = "";
28302  MultiplayerPlayerPanel->Visible = false;
28304  PlayerReadyToBeginFlag = false;
28306  PlayerInSessionFlag = false;
28307  PlayerCancelJoinFlag = true;
28308  Track->MultiplayerOverlayMap.clear();
28310  Utilities->CallLogPop(2341);
28311  }
28312 
28313  catch(const Exception &e)
28314  {
28315  ErrorLog(236, e.Message);
28316  }
28317 }
28318 
28319 //---------------------------------------------------------------------------
28320 
28321 void __fastcall TInterface::MPCPReadyToBeginButtonClick(TObject *Sender)
28322 {
28323  try
28324  {
28325  TrainController->LogEvent("MPCPReadyToBeginButtonClick");
28326  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPReadyToBeginButtonClick");
28327  int button = Application->MessageBox(L"This will transfer all timer controls to the host.\n\n Click 'Yes' to wait for the host to start the simulation.\n\nOK to proceed?", L"Warning!", MB_YESNO | MB_ICONWARNING);
28328  if(button == IDNO)
28329  {
28330  Utilities->CallLogPop(2339);
28331  return;
28332  }
28333 //transfer control etc
28334  PlayerReadyToBeginFlag = true;
28335 //allow certain buttons but not timer controls
28336  CallingOnButton->Enabled = true;
28337  PresetAutoSigRoutesButton->Enabled = true;
28338  AutoSigsButton->Enabled = true;
28339  SigPrefConsecButton->Enabled = true;
28340  SigPrefNonConsecButton->Enabled = true;
28341  UnrestrictedButton->Enabled = true;
28342  RouteCancelButton->Enabled = true;
28343  PerformanceLogButton->Enabled = true;
28344  OperatorActionButton->Enabled = true;
28345  ExitOperationButton->Enabled = true;
28346  OperateButton->Enabled = false;
28347  SaveSessionButton->Enabled = false;
28348  TTClockAdjButton->Enabled = false;
28349 
28350  //await host acknowledgement
28351  Utilities->CallLogPop(2340);
28352  }
28353  catch(const Exception &e)
28354  {
28355  ErrorLog(232, e.Message);
28356  }
28357 }
28358 
28359 //---------------------------------------------------------------------------
28360 
28361 void __fastcall TInterface::MPCPHostIPEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
28362 {
28363  try
28364  {
28365  TrainController->LogEvent("MPCPIPEditBoxKeyUp," + AnsiString(Key));
28366  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPIPEditBoxKeyUp," + AnsiString(Key));
28367  if(Key == '\x0D') //CR
28368  {
28370  {
28371  ShowMessage("Format should be 'a.b.c.d' where a, b, c, and d are numbers between 0 and 255.\n\nObtain the address from the host and enter exactly as given.");
28372  MPCPHostIPEditBox->Text = "";
28373  Utilities->CallLogPop(2360);
28374  return;
28375  }
28376  else
28377  {
28378  MPCPHostIPEditBox->Enabled = false;
28379  if((MPCPHostPortEditBox->Enabled == false) && (MPCPPlayerNameEditBox->Enabled == false))
28380  {
28381  MPPlayerClient->Host = MPCPHostIPEditBox->Text;
28382  MPPlayerClient->Port = MPCPHostPortEditBox->Text.ToInt();
28383  MPCPSendButton->Enabled = true;
28384  }
28385  }
28386  }
28387  else
28388  {
28389  for(int x = 1; x <= MPCPHostIPEditBox->Text.Length(); x++)
28390  {
28391  if((MPCPHostIPEditBox->Text[x] != '.') && ((MPCPHostIPEditBox->Text[x] < '0') || (MPCPHostIPEditBox->Text[x] > '9')))
28392  {
28393  ShowMessage("Format should be 'a.b.c.d' where a, b, c, and d are numbers between 0 and 255.\n\nObtain the address from the host and enter exactly as given.");
28394  MPCPHostIPEditBox->Text = "";
28395  Utilities->CallLogPop(2361);
28396  return;
28397  }
28398  }
28399  }
28400  Utilities->CallLogPop(2362);
28401  }
28402  catch(const Exception &e)
28403  {
28404  ErrorLog(233, e.Message);
28405  }
28406 }
28407 
28408 //---------------------------------------------------------------------------
28409 
28410 void __fastcall TInterface::MPCPHostPortEditBoxKeyUp(TObject *Sender, WORD &Key,
28411  TShiftState Shift)
28412 {
28413  try
28414  {
28415  TrainController->LogEvent("MPCPHostPortEditBoxKeyUp," + AnsiString(Key));
28416  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPHostPortEditBoxKeyUp," + AnsiString(Key));
28417  if(Key == '\x0D') //CR
28418  {
28419  if((MPCPHostPortEditBox->Text == "") || (MPCPHostPortEditBox->Text.Length() < 5) || (MPCPHostPortEditBox->Text.ToInt() < 49152) || (MPCPHostPortEditBox->Text.ToInt() > 59999))
28420  {
28421  ShowMessage("Entry should be a number between 49152 and 59999.\n\nObtain the port number from the host and enter exactly as given.");
28422  MPCPHostPortEditBox->Text = "";
28423  Utilities->CallLogPop(2363);
28424  return;
28425  }
28426  MPCPHostPortEditBox->Enabled = false;
28427  if((MPCPHostIPEditBox->Enabled == false) && (MPCPPlayerNameEditBox->Enabled == false))
28428  {
28429  MPPlayerClient->Host = MPCPHostIPEditBox->Text;
28430  MPPlayerClient->Port = MPCPHostPortEditBox->Text.ToInt();
28431  MPCPSendButton->Enabled = true;
28432  }
28433  }
28434  else
28435  {
28436  for(int x = 1; x <= MPCPHostPortEditBox->Text.Length(); x++)
28437  {
28438  if((MPCPHostPortEditBox->Text[x] < '0') || (MPCPHostPortEditBox->Text[x] > '9'))
28439  {
28440  ShowMessage("Entry should be a number between 49152 and 59999.\n\nObtain the port number from the host and enter exactly as given.");
28441  MPCPHostPortEditBox->Text = "";
28442  Utilities->CallLogPop(2364);
28443  return;
28444  }
28445  }
28446  }
28447  Utilities->CallLogPop(2365);
28448  }
28449  catch(const Exception &e)
28450  {
28451  ErrorLog(234, e.Message);
28452  }
28453 }
28454 //---------------------------------------------------------------------------
28455 
28456 void __fastcall TInterface::MPCPPlayerNameEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
28457 {
28458  try
28459  {
28460  TrainController->LogEvent("MPCPPlayerNameEditBoxKeyUp," + AnsiString(Key));
28461  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MPCPPlayerNameEditBoxKeyUp," + AnsiString(Key));
28462  MPCPLabel3->Caption = "Player name (2 to 4 characters)";
28463  if(MPCPPlayerNameEditBox->Text.Length() > 0)
28464  {
28465  if(Key == '\x0D') //CR
28466  {
28467  if(MPCPPlayerNameEditBox->Text.Length() < 2)
28468  {
28469  ShowMessage("Entry should have at least two and at most four characters with no numbers or special characters.");
28470  MPCPPlayerNameEditBox->Text = "";
28471  Utilities->CallLogPop(2366);
28472  return;
28473  }
28474  else
28475  {
28477  MPCPPlayerNameEditBox->Enabled = false;
28478  if((MPCPHostIPEditBox->Enabled == false) && (MPCPHostPortEditBox->Enabled == false))
28479  {
28480  MPPlayerClient->Host = MPCPHostIPEditBox->Text;
28481  MPPlayerClient->Port = MPCPHostPortEditBox->Text.ToInt();
28482  MPCPSendButton->Enabled = true;
28483  }
28484  }
28485  }
28486  else
28487  {
28488  for(int x = 1; x <= MPCPPlayerNameEditBox->Text.Length(); x++)
28489  {
28490  if((MPCPPlayerNameEditBox->Text[x] < 'A') || ((MPCPPlayerNameEditBox->Text[x] > 'Z') && (MPCPPlayerNameEditBox->Text[x] < 'a'))
28491  || (MPCPPlayerNameEditBox->Text[x] > 'z'))
28492  {
28493  ShowMessage("Entry should have at least two and at most four characters with no numbers or special characters.");
28494  MPCPPlayerNameEditBox->Text = "";
28495  Utilities->CallLogPop(2367);
28496  return;
28497  }
28498  }
28499  }
28500  }
28501  Utilities->CallLogPop(2368);
28502  }
28503  catch(const Exception &e)
28504  {
28505  ErrorLog(235, e.Message);
28506  }
28507 }
28508 
28509 //---------------------------------------------------------------------------
28510 
28511 void TInterface::BuildDatagramFromPlayerMap(int Caller, char marker, AnsiString UserName, TBytes &buffer, TDynamicMap DynamicMap)
28512 { //datagram contents: marker + username + ;(if < 4 chars) + multiple[usernumber + H lower + H higher + V lower + V higher]
28513  // all but marker & username from DynamicMap
28514  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildDatagramFromPlayerMap, " + AnsiString(marker) + ',' + UserName);
28515  int bufCounter;
28516  buffer.Length = 8192; //initial value, reduced later
28517  buffer[0] = marker;
28518  for(bufCounter = 1; bufCounter <= UserName.Length(); bufCounter++)
28519  {
28520  buffer[bufCounter] = UserName[bufCounter];
28521  }
28522  if(UserName.Length() < 4)
28523  {
28524  buffer[bufCounter] = ';'; //marker for end of username
28525  bufCounter++;
28526  }
28527 //AnsiString DMIt->second.ServiceReference
28528 //short DMIt->second.RepeatNumber
28529 //short DMIt->second.TimeToExitSecs
28530 
28531  for(TDynamicMap::iterator DMIt = DynamicMap.begin(); DMIt != DynamicMap.end(); DMIt++)
28532  {
28533  buffer[bufCounter] = DMIt->first.first; //number
28534  bufCounter++;
28535  buffer[bufCounter] = DMIt->first.second.first & 0x00FF; //H lower
28536  bufCounter++;
28537  buffer[bufCounter] = (DMIt->first.second.first & 0xFF00) / 256; //H higher
28538  bufCounter++;
28539  buffer[bufCounter] = DMIt->first.second.second & 0x00FF; //V lower
28540  bufCounter++;
28541  buffer[bufCounter] = (DMIt->first.second.second & 0xFF00) / 256; //v higher
28542  bufCounter++;
28543  DMIt->second.ServiceReference = DMIt->second.ServiceReference.Trim();
28544  for(int x = 1; x <= DMIt->second.ServiceReference.Length(); x++)
28545  {
28546  buffer[bufCounter] = DMIt->second.ServiceReference[x];
28547  bufCounter++;
28548  }
28549  if(DMIt->second.ServiceReference.Length() < 8)
28550  {
28551  buffer[bufCounter] = ';'; //marker for end of service ref
28552  bufCounter++;
28553  }
28554  buffer[bufCounter] = DMIt->second.RepeatNumber & 0x00FF;
28555  bufCounter++;
28556  buffer[bufCounter] = (DMIt->second.RepeatNumber & 0xFF00) / 256;
28557  bufCounter++;
28558  buffer[bufCounter] = DMIt->second.TimeToExitSecs & 0x00FF;
28559  bufCounter++;
28560  buffer[bufCounter] = (DMIt->second.TimeToExitSecs & 0xFF00) / 256;
28561  bufCounter++;
28562 /* add if need to limit buffer length
28563  if(bufCounter > 490) //last value is 507 as starts at 0 & 17 max bytes for each loop, allows 29 exits/entries
28564  {
28565  break;
28566  }
28567 */
28568  }
28569  buffer.Length = bufCounter; //bufCounter incremented value = length since starts at 0
28570  Utilities->CallLogPop(2379);
28571 }
28572 
28573 //---------------------------------------------------------------------------
28574 
28575 bool TInterface::BuildDynamicMapFromPlayerDatagram(int Caller, TDynamicMap &DMap, TBytes Buffer, unsigned char &marker, AnsiString &UserName)
28576 {//datagrams of this type (most) are of form marker (1 byte), username (up to 4 bytes), then a series of TNumHVPairs
28577 //unlikely to be in alphabetical order, build map in same order as datagram
28578  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildDynamicMapFromPlayerDatagram");
28579  TDynamicMapEntry DMEntry;
28580  TNumHVPair NumHVPair;
28581  int y = 5; //highest NumHVPair start position
28582  DMap.clear();
28583  marker = Buffer[0];
28584  UserName = " ";
28585  for(int x = 1; x < 5; x++)
28586  {
28587  if((Buffer[x] < ' ') || (Buffer[x] > '~'))
28588  {
28589  DMap.clear();
28590  Utilities->CallLogPop(2380);
28591  return(false);
28592  }
28593  if(Buffer[x] == ';')
28594  {
28595  break;
28596  }
28597  }
28598  UserName[1] = Buffer[1]; //always at least 1 character
28599  UserName[2] = Buffer[2];
28600  if(UserName[2] == ';') //1 char only
28601  {
28602  UserName.Delete(2,3);
28603  y = 3;
28604  }
28605  else
28606  {
28607  UserName[3] = Buffer[3];
28608  if(UserName[3] == ';') //2 chars
28609  {
28610  UserName.Delete(3,2);
28611  y = 4;
28612  }
28613  else
28614  {
28615  UserName[4] = Buffer[4];
28616  if(UserName[4] == ';') //3 chars
28617  {
28618  UserName.Delete(4,1);
28619  y = 5;
28620  }
28621  }
28622  }
28623  while(y < (Buffer.Length - 9)) //9 allows for zero length service ref
28624  {
28625  TServiceInfo ServInfo; //ServiceReference + RepeatNumber + TimeToExitSecs (need new default values for each loop)
28626  NumHVPair.first = Buffer[y]; //number
28627  NumHVPair.second.first = Buffer[y+ 1] + (256 * Buffer[y + 2]); //H
28628  NumHVPair.second.second = Buffer[y + 3] + (256 * Buffer[y + 4]); //V
28629  if(!NumHVPairCheckOK(NumHVPair))
28630  {
28631  DMap.clear();
28632  Utilities->CallLogPop(2381);
28633  return(false);
28634  }
28635  y += 5;
28636  int z = 0;
28637  while((Buffer[y + z] != ';') && (z < 8))
28638  {
28639  ServInfo.ServiceReference[z + 1] = Buffer[y + z];
28640  z++;
28641  }
28642  ServInfo.ServiceReference = ServInfo.ServiceReference.Trim();
28643  y += ServInfo.ServiceReference.Length();
28644  if(Buffer[y] == ';')
28645  {
28646  y++; //skip past if there is one
28647  }
28648  ServInfo.RepeatNumber = Buffer[y] + (256 * Buffer[y + 1]);
28649  ServInfo.TimeToExitSecs = Buffer[y + 2] + (256 * Buffer[y + 3]);
28650  y += 4;
28651  if(!ServInfo.CheckOK() && ServInfo.ServiceReference != "")
28652  {
28653  DMap.clear();
28654  Utilities->CallLogPop(2382);
28655  return(false);
28656  }
28657  DMEntry.first = NumHVPair;
28658  DMEntry.second = ServInfo;
28659  DMap.insert(DMEntry);
28660  }
28661  Utilities->CallLogPop(2383);
28662  return(true);
28663 }
28664 
28665 //---------------------------------------------------------------------------
28666 
28667 void TInterface::BuildDatagramFromHostMap(int Caller, TBytes &buffer, TDynamicMap DynamicMap)
28668 { //datagram contents: TTTime as 32 bit integer representing hundredths of a second + multiple[usernumber + H lower + H higher + V lower + V higher]
28669  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildDatagramFromHostMap");
28670  int bufCounter = 0;
28671  buffer.Length = 8192; //initial value, reduced later
28672  unsigned int TTTime = double(TrainController->TTClockTime) * 8640000;
28673  buffer[0] = TTTime % 256;
28674  TTTime /= 256;
28675  buffer[1] = TTTime % 256;
28676  TTTime /= 256;
28677  buffer[2] = TTTime % 256;
28678  TTTime /= 256;
28679  buffer[3] = TTTime % 256;
28680  bufCounter += 4;
28681  for(TDynamicMap::iterator DMIt = DynamicMap.begin(); DMIt != DynamicMap.end(); DMIt++)
28682  {
28683  buffer[bufCounter] = DMIt->first.first; //number
28684  bufCounter++;
28685  buffer[bufCounter] = DMIt->first.second.first & 0x00FF; //H lower
28686  bufCounter++;
28687  buffer[bufCounter] = (DMIt->first.second.first & 0xFF00) / 256; //H higher
28688  bufCounter++;
28689  buffer[bufCounter] = DMIt->first.second.second & 0x00FF; //V lower
28690  bufCounter++;
28691  buffer[bufCounter] = (DMIt->first.second.second & 0xFF00) / 256; //v higher
28692  bufCounter++;
28693  DMIt->second.ServiceReference = DMIt->second.ServiceReference.Trim();
28694  for(int x = 1; x <= DMIt->second.ServiceReference.Length(); x++)
28695  {
28696  buffer[bufCounter] = DMIt->second.ServiceReference[x];
28697  bufCounter++;
28698  }
28699  if(DMIt->second.ServiceReference.Length() < 8)
28700  {
28701  buffer[bufCounter] = ';'; //marker for end of service ref
28702  bufCounter++;
28703  }
28704  buffer[bufCounter] = DMIt->second.RepeatNumber & 0x00FF;
28705  bufCounter++;
28706  buffer[bufCounter] = (DMIt->second.RepeatNumber & 0xFF00) / 256;
28707  bufCounter++;
28708  buffer[bufCounter] = DMIt->second.TimeToExitSecs & 0x00FF;
28709  bufCounter++;
28710  buffer[bufCounter] = (DMIt->second.TimeToExitSecs & 0xFF00) / 256;
28711  bufCounter++;
28712  }
28713  buffer.Length = bufCounter; //bufCounter incremented value = length since starts at 0
28714  Utilities->CallLogPop(2394);
28715 }
28716 
28717 //---------------------------------------------------------------------------
28718 
28719 bool TInterface::BuildDynamicMapFromHostDatagram(int Caller, int TTTime, TDynamicMap &DMap, TBytes Buffer)
28720 {
28721  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildDynamicMapFromHostDatagram");
28722  TDynamicMapEntry DMEntry;
28723  TNumHVPair NumHVPair;
28724  int y = 4; //NumHVPair start position
28725  DMap.clear();
28726  TTTime = Buffer[0] + (256 * Buffer[1]) + (65536 * Buffer[2]) + (16777216 * Buffer[3]);
28727  if((TTTime < 0) || (TTTime > 34560000)) //34560000 = hundredths of a second in 4 x 24 hrs
28728  {
28729  DMap.clear();
28730  Utilities->CallLogPop(2396);
28731  return(false);
28732  }
28733  while(y < (Buffer.Length - 9)) //9 allows for zero length service ref
28734  {
28735  TServiceInfo ServInfo; //ServiceReference + RepeatNumber + TimeToExitSecs (need new default values for each loop)
28736  NumHVPair.first = Buffer[y]; //number
28737  if(NumHVPair.first != OwnRlyUserNumber)
28738  {
28739  DMap.clear();
28740  Utilities->CallLogPop(2397);
28741  return(false);
28742  }
28743  NumHVPair.second.first = Buffer[y+ 1] + (256 * Buffer[y + 2]); //H
28744  NumHVPair.second.second = Buffer[y + 3] + (256 * Buffer[y + 4]); //V
28745  if(!NumHVPairCheckOK(NumHVPair))
28746  {
28747  DMap.clear();
28748  Utilities->CallLogPop(2398);
28749  return(false);
28750  }
28751  y += 5;
28752  int z = 0;
28753  while((Buffer[y + z] != ';') && (z < 8))
28754  {
28755  ServInfo.ServiceReference[z + 1] = Buffer[y + z];
28756  z++;
28757  }
28758  ServInfo.ServiceReference = ServInfo.ServiceReference.Trim();
28759  y += ServInfo.ServiceReference.Length();
28760  if(Buffer[y] == ';')
28761  {
28762  y++; //skip past if there is one
28763  }
28764  ServInfo.RepeatNumber = Buffer[y] + (256 * Buffer[y + 1]);
28765  ServInfo.TimeToExitSecs = Buffer[y + 2] + (256 * Buffer[y + 3]);
28766  y += 4;
28767  if(!ServInfo.CheckOK() && ServInfo.ServiceReference != "")
28768  {
28769  DMap.clear();
28770  Utilities->CallLogPop(2399);
28771  return(false);
28772  }
28773  DMEntry.first = NumHVPair;
28774  DMEntry.second = ServInfo;
28775  DMap.insert(DMEntry);
28776  }
28777  Utilities->CallLogPop(2400);
28778  return(true);
28779 }
28780 
28781 //---------------------------------------------------------------------------
28782 
28783 void TInterface::BuildDummyTestMap(TDynamicMap &DMap, std::ifstream &ExitFile) //<--temporary function for testing purposes
28784 { //ExitFile is a list of H & V values as char[] integers with '\n' delimiters between each and between each pair
28785  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",BuildDummyTestMap");
28786  TDynamicMapEntry DMapEntry;
28787  TServiceInfo ServiceInfo; //null & zero values on construction
28788  THVShortPair ShortPair;
28789  unsigned char UserNum;
28790  while(!ExitFile.eof())
28791  {
28792  ExitFile >> UserNum; //number
28793  UserNum -= 48; //convert from character to ordinal
28794  if(ExitFile.eof()) //only get eof after an unsuccessful read attempt
28795  {
28796  continue;
28797  }
28798  ExitFile >> ShortPair.first; //H
28799  if(ExitFile.eof()) //only get eof after an unsuccessful read attempt
28800  {
28801  continue;
28802  }
28803  ExitFile >> ShortPair.second; //V
28804  if(ExitFile.eof())
28805  {
28806  continue;
28807  }
28808  DMapEntry.first.first = UserNum;
28809  DMapEntry.first.second = ShortPair;
28810  DMapEntry.second = ServiceInfo;
28811  DMap.insert(DMapEntry);
28812  }
28813  Utilities->CallLogPop(2384);
28814 }
28815 
28816 //---------------------------------------------------------------------------
28817 
28819 {
28820 //The player calls this prior to sending DynMapToHost, i.e. all the coupled linkages with railwaynumbers
28821 //typedef std::pair<TNumHVPair, TServiceInfo> TDynamicMapEntry;
28822 //typedef std::multimap<TNumHVPair, TExitInfo> TTimeToExitMultiMap;
28823  Utilities->CallLog.push_back(Utilities->TimeStamp() + "UpdateDynamicMapFromTimeToExitMultiMap");
28824  TDynamicMapEntry DMEntry;
28825  TDynamicMap::iterator DMIt;
28826  TNumHVPair NumHVPair;
28827 
28828  TTimeToExitMultiMap::iterator TTEMMIt = TrainController->TimeToExitMultiMap.begin();
28829  while(TTEMMIt != TrainController->TimeToExitMultiMap.end())
28830  {
28831  NumHVPair.first = OwnRlyUserNumber;
28832  NumHVPair.second = TTEMMIt->first;
28833  DMIt = DMap.find(NumHVPair);
28834  if(DMIt != DMap.end()) //else skip it, not included in coupling map
28835  {
28836  DMIt->second.ServiceReference = TTEMMIt->second.ServiceReference.Trim();
28837  DMIt->second.RepeatNumber = TTEMMIt->second.RepeatNumber;
28838  DMIt->second.TimeToExitSecs = TTEMMIt->second.TimeToExitSecs;
28839  }
28840  TTEMMIt++;
28841  }
28842  Utilities->CallLogPop(2385);
28843 }
28844 
28845 //---------------------------------------------------------------------------
28846 
28847 bool TInterface::InvalidIPAddress(AnsiString Text)
28848 {
28849  try
28850  {
28851  int Dot1 = 0, Dot2 = 0, Dot3 = 0;
28852  AnsiString Num1 = "",Num2 = "",Num3 = "",Num4 = "";
28853  if(Text.Length() < 7)
28854  {
28855  return true;
28856  }
28857  for(int x = 1; x <= Text.Length(); x++)
28858  {
28859  if(Text[x] == '.')
28860  {
28861  if(Dot1 == 0)
28862  {
28863  Dot1 = x;
28864  }
28865  else if(Dot2 == 0)
28866  {
28867  Dot2 = x;
28868  }
28869  else if(Dot3 == 0)
28870  {
28871  Dot3 = x;
28872  }
28873  }
28874  }
28875  if((Dot1 ==0) || (Dot2 ==0) || (Dot3 ==0))
28876  {
28877  return true; //less than 3 dots
28878  }
28879  if(Text.Length() == Dot3)
28880  {
28881  return true; //last dot at end
28882  }
28883  Num1 = Text.SubString(1, Dot1 - 1);
28884  Num2 = Text.SubString(Dot1 + 1, Dot2 - Dot1 - 1);
28885  Num3 = Text.SubString(Dot2 + 1, Dot3 - Dot2 - 1);
28886  Num4 = Text.SubString(Dot3 + 1, Text.Length() - Dot3);
28887  if((Num1 == "") || (Num2 == "") || (Num3 == "") || (Num4 == ""))
28888  {
28889  return true; //any number string empty
28890  }
28891  if((Num1.ToInt() > 255) ||(Num2.ToInt() > 255) ||(Num3.ToInt() > 255) ||(Num4.ToInt() > 255)) //EConvertError thrown if not an integer
28892  {
28893  return true;
28894  }
28895  if((Num1.ToInt() < 0) ||(Num2.ToInt() < 0) ||(Num3.ToInt() < 0) ||(Num4.ToInt() < 0))
28896  {
28897  return true;
28898  }
28899  return false;
28900  }
28901  catch(const EConvertError &e) //non-integer should have been caught earlier but include for completeness
28902  {
28903  return true;
28904  }
28905 }
28906 
28907 //---------------------------------------------------------------------------
28908 
28909 TInterface::TServiceInfo::TServiceInfo() //default constructor , same as TExitInfo() in TrainUnit
28910 {
28911  ServiceReference = " ";
28912  RepeatNumber = 0;
28913  TimeToExitSecs = -1;
28914 }
28915 
28916 //---------------------------------------------------------------------------
28917 
28919 {
28920  RlyUserNumber = 0; //allocated numbers start from 1
28921  RailwayName = "";
28922  UserName = "";
28923  UserIP = "";
28924  UserPort = 0;
28925 }
28926 
28927 //---------------------------------------------------------------------------
28928 
28929 bool TInterface::RlyToNum(AnsiString RailwayName, unsigned char &RlyUserNumber)
28930 {
28931  if(InfoVector.empty())
28932  {
28933  return(false);
28934  }
28935  TIVIt IVIt;
28936  for(IVIt = InfoVector.begin(); IVIt != InfoVector.end(); IVIt++)
28937  {
28938  if(IVIt->RailwayName == RailwayName)
28939  {
28940  RlyUserNumber = IVIt->RlyUserNumber;
28941  return(true);
28942  }
28943  }
28944  return(false);
28945 }
28946 
28947 //---------------------------------------------------------------------------
28948 
28949 bool TInterface::UserToNum(AnsiString UserName, unsigned char &RlyUserNumber)
28950 {
28951  if(InfoVector.empty())
28952  {
28953  return(false);
28954  }
28955  TIVIt IVIt;
28956  for(IVIt = InfoVector.begin(); IVIt != InfoVector.end(); IVIt++)
28957  {
28958  if(IVIt->UserName == UserName)
28959  {
28960  RlyUserNumber = IVIt->RlyUserNumber;
28961  return(true);
28962  }
28963  }
28964  return(false);
28965 }
28966 
28967 //---------------------------------------------------------------------------
28968 
28969 bool TInterface::NumToRly(unsigned char RlyUserNumber, AnsiString &RailwayName)
28970 {
28971  if(InfoVector.empty())
28972  {
28973  return(false);
28974  }
28975  TIVIt IVIt;
28976  for(IVIt = InfoVector.begin(); IVIt != InfoVector.end(); IVIt++)
28977  {
28978  if(IVIt->RlyUserNumber == RlyUserNumber)
28979  {
28980  RailwayName = IVIt->RailwayName;
28981  return(true);
28982  }
28983  }
28984  return(false);
28985 }
28986 
28987 //---------------------------------------------------------------------------
28988 
28989 bool TInterface::UserToIPAndPort(AnsiString UserName, AnsiString &UserIP, short UserPort)
28990 {
28991  if(InfoVector.empty())
28992  {
28993  return(false);
28994  }
28995  TIVIt IVIt;
28996  for(IVIt = InfoVector.begin(); IVIt != InfoVector.end(); IVIt++)
28997  {
28998  if(IVIt->UserName == UserName)
28999  {
29000  UserIP = IVIt->UserIP;
29001  UserPort = IVIt->UserPort;
29002  return(true);
29003  }
29004  }
29005  return(false);
29006 }
29007 
29008 //---------------------------------------------------------------------------
29009 
29010 bool TInterface::NumToIPAndPort(unsigned char RlyUserNumber, AnsiString &UserIP, short UserPort)
29011 {
29012  if(InfoVector.empty())
29013  {
29014  return(false);
29015  }
29016  TIVIt IVIt;
29017  for(IVIt = InfoVector.begin(); IVIt != InfoVector.end(); IVIt++)
29018  {
29019  if(IVIt->RlyUserNumber == RlyUserNumber)
29020  {
29021  UserIP = IVIt->UserIP;
29022  UserPort = IVIt->UserPort;
29023  return(true);
29024  }
29025  }
29026  return(false);
29027 }
29028 
29029 //---------------------------------------------------------------------------
29030 
29032 {
29033  if(NumHVPair.first > 100) //100 players max
29034  {
29035  return(false);
29036  }
29037  if((NumHVPair.second.first > 2000) || (NumHVPair.second.first < -2000))
29038  {
29039  return(false);
29040  }
29041  if((NumHVPair.second.second > 2000) || (NumHVPair.second.second < -2000))
29042  {
29043  return(false);
29044  }
29045  return(true);
29046 }
29047 
29048 //---------------------------------------------------------------------------
29049 
29051 {
29052  if(!TrainController->CheckHeadCodeValidity(13, false, ServiceReference)) //false for no messages
29053  {
29054  return(false);
29055  }
29056  if((RepeatNumber < 0) || (RepeatNumber > 6000)) //allows minute repeats for 4 days
29057  {
29058  return(false);
29059  }
29060  if((TimeToExitSecs < -1) || (TimeToExitSecs > 3600))
29061  {
29062  return(false);
29063  }
29064  return(true);
29065 }
29066 
29067 //---------------------------------------------------------------------------
29068 
29069 void TInterface::RemovePlayerFromStringGridAndInfoVector(int Caller, AnsiString PlayerUserName)
29070 {
29071  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",RemovePlayerFromStringGridAndInfoVector, " + PlayerUserName);
29072  for(int x = 0; x < NumPlayers; x++)
29073  {
29074  if(MultiplayerHostStringGrid->Cells[0][x + 2] == PlayerUserName)
29075  {
29076  MultiplayerHostStringGrid->Cells[0][x + 2] = "";
29077  MultiplayerHostStringGrid->Cells[2][x + 2] = "No";
29078  break;;
29079  }
29080  }
29081  if(!InfoVector.empty())//clear InfoVector of all but railway name
29082  {
29083  for(int x = 0; x < NumPlayers; x++)
29084  {
29085  if(InfoVector.at(x).UserName == PlayerUserName)
29086  {
29087 // InfoVector.at(x).RlyUserNumber = 0; //no, the number stays as is
29088  InfoVector.at(x).UserName = "";
29089  InfoVector.at(x).UserIP = "";
29090  InfoVector.at(x).UserPort = 0;
29091  break;;
29092  }
29093  }
29094  }
29095  Utilities->CallLogPop(2390);
29096 }
29097 
29098 //---------------------------------------------------------------------------
29099 
29100 void __fastcall TInterface::ShowHideStringGridMenuItemClick(TObject *Sender)
29101 {
29102  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",ShowHideStringGridMenuItemClick");
29103  TrainController->LogEvent("ShowHideStringGridMenuItemClick");
29104  if(MultiplayerHostStringGrid->Visible)
29105  {
29106  MultiplayerHostStringGrid->Visible = false;
29107  }
29108  else
29109  {
29110  MultiplayerHostStringGrid->Visible = true;
29111  }
29112  Utilities->CallLogPop(2413);
29113 }
29114 
29115 //---------------------------------------------------------------------------
29116 
29118 {
29119  try
29120  {
29121  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",PlayerHandshakingActions");
29122  if(PlayerFiveSecondTimer == 0) //send at 5 sec intervals
29123  {
29124  EIdExceptionSource = "PlayerFiveSecondTimer";
29125  if(PlayerMakingInitialContactFlag) //sends it every 5 secs until host responds & then PlayerMakingInitialContactFlag reset
29126  {
29127  EIdExceptionSource += " PlayerMakingInitialContactFlag";
29128  //load datagram - Marker ('1') + UserName + ';' + RailwayName (no space or delimier between marker & railway name)
29129  UnicodeString Data = UnicodeString('1') + MPCPUserName + UnicodeString(';') + RailwayTitle;
29130  MPPlayerClient->Send(Data);
29131  }
29132  else if(PlayerReadyToBeginFlag)
29133  {
29134  EIdExceptionSource += " PlayerReadyToBeginFlag";
29135  //load datagram - Marker = '2' + UserName + times to exit (service ref, exit H&V & time) from DynMapToHost
29136  TBytes buffer;
29139  MPPlayerClient->SendBuffer(MPPlayerClient->Host, MPPlayerClient->Port, buffer);
29141  LastHostDataReceived = TDateTime(0);
29142  }
29143  else if(PlayerInSessionFlag)
29144  {
29145  EIdExceptionSource += " PlayerInSessionFlag5SecTimer";
29146  TBytes buffer;
29148  BuildDatagramFromPlayerMap(1, '5', MPCPUserName, buffer, DynMapToHost); //buffer length is set within this function
29149  MPPlayerClient->SendBuffer(MPPlayerClient->Host, MPPlayerClient->Port, buffer);
29150  //check last message time
29151  if(((TDateTime::CurrentDateTime() - LastHostDataReceived) > TDateTime(0.00034722)) && (LastHostDataReceived > TDateTime(1))) //== 30secs
29152  {
29153  //update all times to entry by 30 secs & have any enter that are due in that time (missed at least 4)
29154  //also when any train enters check that it's not already entered and ignore if so (Service ref + repeat number defines)
29156  if(ConsecutiveSelfUpdates > 10) //at least 5 minutes with no contact
29157  {
29158  //cancel session for this player
29159  }
29160  }
29161  }
29162  else
29163  {
29164  //default, probably won't need it
29165  }
29166  }
29167 
29168  if(PlayerOneSecondTimer == 10) //send at 1 sec intervals, use 10 so out of step with 5 sec timer
29169  {
29170  if(PlayerCancelJoinFlag) //send every second until acknowledged
29171  {
29172  //send message to host, marker = 3, username; nothing else, '3' tells host of the cancellation
29173  EIdExceptionSource += " PlayerCancelJoinFlag";
29174  //load datagram - Marker ('3') + UserName
29175  UnicodeString Data = UnicodeString('3') + MPCPUserName;
29176  MPPlayerClient->Send(Data);
29177  }
29179  {
29180  EIdExceptionSource += " PlayerAwaitingHostStartFlag";
29181  //load datagram - Marker ('4') + UserName
29182  UnicodeString Data = UnicodeString('4') + MPCPUserName;
29183  MPPlayerClient->Send(Data);
29184  }
29185  else if(PlayerInSessionFlag)
29186  {
29187  EIdExceptionSource += " PlayerInSessionFlag1SecTimer";
29188  UnicodeString Data = UnicodeString('6') + MPCPUserName;
29189  MPPlayerClient->Send(Data);
29190  }
29191  }
29192 
29193 //here player checks for any messages from host - after intial contact or speed changes etc during multiplay (every cycle)
29194  if(PlayerMakingInitialContactFlag) //receive
29195  {
29196  EIdExceptionSource = "PlayerMakingInitialContactFlag every cycle";
29197 // UnicodeString HostMessage = MPPlayerClient->ReceiveString(10);
29198  TBytes Buffer;
29199  Buffer.Length = 8192; //have to set long enough initially, reduced later
29200  Buffer.Length = MPPlayerClient->ReceiveBuffer(Buffer, 10); //<-- ReceiveBuffer
29201  if(Buffer.Length != 0)
29202  {
29203  PlayerMakingInitialContactFlag = false; //only if reply received
29204  if((Buffer[0] == 'T') && (Buffer[1] == 'h') && (Buffer[2] == 'e')) //error message
29205  {
29206  UnicodeString HostMessage = BytesToString(Buffer);
29207  ShowMessage(HostMessage);
29208  MPCPSendButton->Enabled = false;
29209  MPCPReadyToBeginButton->Enabled = false;
29210  MPCPPlayerNameEditBox->Text = "";
29211  MPCPHostPortEditBox->Text = "";
29212  MPCPHostIPEditBox->Text = "";
29213  PlayerReadyToBeginFlag = false;
29214  PlayerCancelJoinFlag = false;
29216  PlayerInSessionFlag = false;
29217  MultiplayerPlayerPanel->Visible = false;
29218  Track->MultiplayerOverlayMap.clear();
29220  }
29221  else //receive 'raw' coupled map for this player - own NumHVPair + coupled NumHVPair with default service info
29222  { //compile initial DynMapFromHost & DynMapToHost, & add overlays to the coupled exit graphics
29223  TServiceInfo ServiceInfo; //default values
29224  THVShortPair ShortPair;
29225  unsigned char UserNumber;
29226  TDynamicMapEntry DMEntry;
29227  DynMapFromHost.clear();
29228  DynMapToHost.clear();
29229  OwnRlyUserNumber = Buffer[0];
29230  for(int x = 0; x < (Buffer.Length - 9); x += 10)
29231  {
29232  UserNumber = Buffer[x];
29233  ShortPair.first = Buffer[x + 1] + (256 * Buffer[x + 2]);
29234  ShortPair.second = Buffer[x + 3] + (256 * Buffer[x + 4]);
29235  DMEntry.first.first = UserNumber;
29236  DMEntry.first.second = ShortPair;
29237  DMEntry.second = ServiceInfo;
29238  DynMapFromHost.insert(DMEntry);
29239  UserNumber = Buffer[x + 5];
29240  ShortPair.first = Buffer[x + 6] + (256 * Buffer[x + 7]);
29241  ShortPair.second = Buffer[x + 8] + (256 * Buffer[x + 9]);
29242  DMEntry.first.first = UserNumber;
29243  DMEntry.first.second = ShortPair;
29244  DMEntry.second = ServiceInfo;
29245  DynMapToHost.insert(DMEntry);
29246  }
29247  //Build Track->MultiplayerOverlayMap from DynMapFromHost, it consists of key = THVPair & value = graphic pointer
29248  Track->MultiplayerOverlayMap.clear();
29249  for(TDMIt DMIt = DynMapFromHost.begin(); DMIt != DynMapFromHost.end(); DMIt++)
29250  {
29251  typedef std::pair<int, int> THLocVLocPair;
29252  THLocVLocPair HVPair;
29253  std::pair<THLocVLocPair, Graphics::TBitmap*> MPOMEntry;
29254  int SpeedTag;
29255  Graphics::TBitmap *GrPtr;
29256  HVPair.first = int(DMIt->first.second.first);
29257  HVPair.second = int(DMIt->first.second.second);
29258  SpeedTag = Track->GetTrackElementFromTrackMap(4, HVPair.first, HVPair.second).SpeedTag;
29259  if((SpeedTag < 80) || (SpeedTag > 87))
29260  {
29261  throw Exception("Error, SpeedTag not a continuation in DynMapFromHost, value is " + AnsiString(SpeedTag));
29262  }
29263  else if(SpeedTag == 80)
29264  {
29265  GrPtr = RailGraphics->CouplingExit4;
29266  }
29267  else if(SpeedTag == 81)
29268  {
29269  GrPtr = RailGraphics->CouplingExit6;
29270  }
29271  else if(SpeedTag == 82)
29272  {
29273  GrPtr = RailGraphics->CouplingExit8;
29274  }
29275  else if(SpeedTag == 83)
29276  {
29277  GrPtr = RailGraphics->CouplingExit2;
29278  }
29279  else if(SpeedTag == 84)
29280  {
29281  GrPtr = RailGraphics->CouplingExit1;
29282  }
29283  else if(SpeedTag == 85)
29284  {
29285  GrPtr = RailGraphics->CouplingExit3;
29286  }
29287  else if(SpeedTag == 86)
29288  {
29289  GrPtr = RailGraphics->CouplingExit7;
29290  }
29291  else if(SpeedTag == 87)
29292  {
29293  GrPtr = RailGraphics->CouplingExit9;
29294  }
29295  MPOMEntry.first = HVPair;
29296  MPOMEntry.second = GrPtr;
29297  Track->MultiplayerOverlayMap.insert(MPOMEntry);
29298  }
29300  MPCPLabel2->Caption = "When ready click 'Ready to begin'"; //top box
29301  MPCPLabel4->Caption = "Joining request accepted"; //opposite image
29302  MPCPLabel7->Caption = ""; //Lower box top
29303  MPCPLabel8->Caption = ""; //Lower box bottom
29304  MPCPHostImage->Picture->Assign(RailGraphics->SolidCircleGreen);
29305  MPCPReadyToBeginButton->Enabled = true;
29306  }
29307  }
29308  }
29309  else if(PlayerReadyToBeginFlag)
29310  {
29311  EIdExceptionSource = "PlayerReadyToBegin every cycle";
29312  TBytes Buffer;
29313  Buffer.Length = 8192; //have to set long enough initially, reduced later
29314  Buffer.Length = MPPlayerClient->ReceiveBuffer(Buffer, 10); //<-- ReceiveBuffer
29315  if(Buffer.Length != 0)
29316  {
29317  if(BytesToString(Buffer) == "Await simulation start")
29318  {
29319  PlayerReadyToBeginFlag = false;
29321  MPCPReadyToBeginButton->Enabled = false;
29322  MPCPLabel2->Caption = "Awaiting simulation start"; //top box
29323  MPCPLabel4->Caption = ""; //opposite image
29324  MPCPLabel7->Caption = ""; //Lower box top
29325  MPCPLabel8->Caption = ""; //Lower box bottom
29326  MPCPLabel5->Caption = ""; //Host IP
29327  MPCPLabel6->Caption = ""; //Host Port
29328  MPCPLabel3->Caption = ""; //UserName
29329  MPCPHostIPEditBox->Visible = false;
29330  MPCPHostPortEditBox->Visible = false;
29331  MPCPPlayerNameEditBox->Visible = false;
29332  MPCPHostImage->Visible = false;
29333  }
29334  else
29335  {
29336  //do nothing, leave PlayerReadyToStart true & send message again
29337  }
29338  }
29339  else
29340  {
29341  //do nothing, leave PlayerReadyToStart true & send message again
29342  }
29343  }
29344  else if(PlayerCancelJoinFlag)
29345  {
29346  EIdExceptionSource = "PlayerCancelJoinFlag every cycle";
29347  TBytes Buffer;
29348  Buffer.Length = 8192; //have to set long enough initially, reduced later
29349  Buffer.Length = MPPlayerClient->ReceiveBuffer(Buffer, 10); //<-- ReceiveBuffer
29350  if(Buffer.Length != 0)
29351  {
29352  if(BytesToString(Buffer) == "Cancelled")
29353  {
29354  PlayerCancelJoinFlag = false;
29355  }
29356  }
29357  }
29358  else if(PlayerAwaitingHostStartFlag) //await start message then clear the joining box, set PlayerMultiplayerInSession & reset PlayerAwaitingHostStart
29359  {
29360  EIdExceptionSource = "PlayerAwaitingHostStartFlag every cycle";
29361  TBytes Buffer;
29362  Buffer.Length = 8192; //have to set long enough initially, reduced later
29363  Buffer.Length = MPPlayerClient->ReceiveBuffer(Buffer, 10); //<-- ReceiveBuffer
29364  if(Buffer.Length != 0)
29365  {
29366  if(BytesToString(Buffer) == "Start Session")
29367  {
29369  PlayerInSessionFlag = true;
29370  MultiplayerPlayerPanel->Visible = false;
29371  OperatingPanelLabel->Caption = "Operation";
29372  OperateButton->Click(); //keep speed etc. buttons disabled
29373  }
29374  }
29375  }
29376  else if(PlayerInSessionFlag)
29377  {
29378  EIdExceptionSource = "PlayerMultiplayerInSession every cycle";
29379  TBytes Buffer;
29380  int TTTime;
29381  TDynamicMap DMap;
29382  TDynamicMap::iterator DMIt, DMFHIt;
29383  Buffer.Length = 8192; //have to set long enough initially, reduced later
29384  Buffer.Length = MPPlayerClient->ReceiveBuffer(Buffer, 10); //<-- ReceiveBuffer
29385  if(Buffer.Length != 0)
29386  {
29387  //here have DynMapFromHost as buffer with TTClockTime at start as 32 bit integer
29389  if(BuildDynamicMapFromHostDatagram(0, TTTime, DMap, Buffer)) //validity checks in this function
29390  {
29391  //store TTTime & update DynMapFromHost, should be in proper order but don't rely on it
29392  TrainController->TTClockTime = double(TTTime) / 8640000;
29393  for(DMIt = DMap.begin(); DMIt != DMap.end(); DMIt++)
29394  {
29395  DMFHIt = DynMapFromHost.find(DMIt->first);
29396  if(DMFHIt != DynMapFromHost.end())
29397  {
29398  DMFHIt->second = DMIt->second;
29399  }
29400  //else skip it
29401  }
29402  LastHostDataReceived = TDateTime::CurrentDateTime();
29403 
29404  //now have TTClock updated + service refs & times to entry in DynMapFromHost
29405 
29406  }
29407  //else skip it - catch up next time
29408  }
29409  }
29410  Utilities->CallLogPop(2416);
29411  }
29412  catch(const EIdException &e) //non-error catch
29413 //if no response from peer then get a 'connection reset by peer' message which isn't valid
29414  {
29415  Utilities->CallLogPop(2417);
29416  }
29417  catch(const Exception &e)
29418  {
29419  ErrorLog(240, e.Message);
29420  }
29421 }
29422 
29423 //---------------------------------------------------------------------------
29424 
29425 void __fastcall TInterface::LengthsHeatmapButtonClick(TObject *Sender) //only available when length/speed setting //added at v2.22.0
29426 /*
29427 Heatmap functions:
29428 
29429 Use a small utility function in InterfaceUnit to initialise all heatmap parameters - ExitHeatmaps()
29430 
29431 ExitHeatmaps() used for the following TrackBuild buttons
29432  AddTrackButton;
29433  SetGapsButton;
29434  TrackOKButton;
29435  AddTextButton;
29436  MoveTextOrGraphicButton;
29437  LocationNameButton;
29438  SaveRailwayTBPButton; (SaveHeaderMeni1Click)
29439  ExitTrackButton;
29440  UserGraphicButton;
29441 
29442 No action needed for the following
29443  FontButton;
29444  TextOrUserGraphicGridButton;
29445  ScreenGridButton;
29446  SigAspectButton;
29447  RestoreAllDefaultLengthsButton; heatmap buttons disabled when this is available
29448  ResetDefaultLengthButton; heatmap buttons disabled when this is available
29449  LengthCancelButton; heatmap buttons disabled when this is available
29450  LengthOKButton; heatmap buttons disabled when this is available
29451 
29452 Actions needed for the following
29453  SetLengthsButton;
29454  when first invoke enter case DistanceStart, here both buttons visible & enabled
29455  when enter Distance continuing (for extending selection for length or speed setting)
29456  buttons become disabled, re-enabled when exit DistanceContinuing when re-enter
29457  DistanceStart
29458 
29459  click LengthHeatMapBitBtn (with LengthheatMapFlag true)
29460 
29461  ... non heatmap code
29462  ExitHeatmaps(); //initialise
29463  Track->LengthHeatMapFlag = true; //shows heatmap when call Clearand...
29464  LengthHeatmapBitBtn->Caption = "Hide Track Length Heatmap";
29465  LengthHeatmapBitBtn->Visible = true;
29466  LengthHeatmapBitBtn->Enabled = true;
29467  SpeedHeatmapBitBtn->Visible = true;
29468  LengthHeatMapImage->Visible = true;
29469  DistanceKey->Visible = false;
29470  ... non heatmap code
29471 
29472  click LengthHeatMapBitBtn (with LengthheatMapFlag false)
29473 
29474  ExitHeatmaps(); //initialise
29475  DistanceKey->Visible = true;
29476  ... non heatmap code (all heatmap requirements catered for when DistanceStart called)
29477 
29478  click SpeedHeatMapBitBtn - as above with Length->Speed & vice versa
29479 
29480 
29481  ClearandRebuildRailway
29482  Calls RebuildTrackAndText and this calls Track->OneLengthOrSpeedHeatMapColour which handles the appropriate
29483  heatmap depending on the HeatMapFlag settings. The colour generation is done in GraphicsUnit in function GetHeatMapColor
29484 */
29485 {
29486  try
29487  {
29488  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",LengthsHeatmapButtonClick");
29489  if(!Track->LengthHeatMapFlag)
29490  {
29491  InfoPanel->Visible = true;
29492  Screen->Cursor = TCursor(-11); // Hourglass
29493  InfoPanel->Caption = "Please wait - element length heatmap being generated";
29494  Display->Update();
29495  ExitHeatmaps(); //initialise
29496  Track->LengthHeatMapFlag = true; //shows heatmap when call Clearand...
29497  LengthHeatmapBitBtn->Caption = "Hide Track Length Heatmap";
29498  LengthHeatmapBitBtn->Visible = true;
29499  LengthHeatmapBitBtn->Enabled = true;
29500  SpeedHeatmapBitBtn->Visible = true;
29501  LengthHeatMapImage->Visible = true;
29502  DistanceKey->Visible = false;
29503  DistancesMarked = false;
29504  Display->Update(); //to display buttons prior to Clearand.. which can take quite a long time
29506  InfoPanel->Caption = "Track element length heatmap";
29507  Screen->Cursor = TCursor(-2); // Arrow
29508  }
29509  else
29510  {
29511  ExitHeatmaps(); //initialise, all heatmap settings catered for in DistanceStart
29512  Screen->Cursor = TCursor(-11); // Hourglass
29513  DistanceKey->Visible = true;
29515  SetLevel2TrackMode(70);
29517  Screen->Cursor = TCursor(-2); // Arrow
29518  }
29519  Utilities->CallLogPop(2720);
29520  }
29521  catch(const Exception &e)
29522  {
29523  ErrorLog(251, e.Message);
29524  }
29525 }
29526 
29527 //---------------------------------------------------------------------------
29528 
29529 void __fastcall TInterface::SpeedsHeatmapButtonClick(TObject *Sender) //only available when length/speed setting //added at v2.22.0
29530 {
29531  try
29532  {
29533  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",SpeedsHeatmapButtonClick");
29534  if(!Track->SpeedHeatMapFlag)
29535  {
29536  InfoPanel->Visible = true;
29537  Screen->Cursor = TCursor(-11); // Hourglass
29538  InfoPanel->Caption = "Please wait - element speed limit heatmap being generated";
29539  Display->Update();
29540  ExitHeatmaps(); //initialise
29541  Track->SpeedHeatMapFlag = true; //shows heatmap when call Clearand...
29542  SpeedHeatmapBitBtn->Caption = "Hide Speed Limit Heatmap";
29543  SpeedHeatmapBitBtn->Visible = true;
29544  SpeedHeatmapBitBtn->Enabled = true;
29545  LengthHeatmapBitBtn->Visible = true;
29546  SpeedHeatMapImage->Visible = true;
29547  DistanceKey->Visible = false;
29548  DistancesMarked = false;
29549  Display->Update(); //to display buttons prior to Clearand.. which can take quite a long time
29551  InfoPanel->Caption = "Track element speed limit heatmap";
29552  Screen->Cursor = TCursor(-2); // Arrow
29553  }
29554  else
29555  {
29556  ExitHeatmaps(); //initialise
29557  Screen->Cursor = TCursor(-11); // Hourglass
29558  DistanceKey->Visible = true;
29560  SetLevel2TrackMode(71);
29562  Screen->Cursor = TCursor(-2); // Arrow
29563  }
29564  Utilities->CallLogPop(2718);
29565  }
29566  catch(const Exception &e)
29567  {
29568  ErrorLog(252, e.Message);
29569  }
29570 }
29571 
29572 //---------------------------------------------------------------------------
29573 
29575 {
29576  Track->LengthHeatMapFlag = false;
29577  Track->SpeedHeatMapFlag = false;
29578  LengthHeatmapBitBtn->Caption = "Show Track Length Heatmap";
29579  SpeedHeatmapBitBtn->Caption = "Show Speed Limit Heatmap";
29580  LengthHeatmapBitBtn->Visible = false;
29581  SpeedHeatmapBitBtn->Visible = false;
29582  LengthHeatmapBitBtn->Enabled = false;
29583  SpeedHeatmapBitBtn->Enabled = false;
29584  LengthHeatMapImage->Visible = false;
29585  SpeedHeatMapImage->Visible = false;
29586 }
29587 
29588 //---------------------------------------------------------------------------
29589 
29590 void __fastcall TInterface::HeatmapsRedlowvaluesMenuItemClick(TObject *Sender)
29591 {
29592  TrainController->LogEvent("HeatmapsRedlowvaluesMenuItemClick");
29593  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",HeatmapsRedlowvaluesMenuItemClick");
29594  if(Utilities->RedLowFlag)
29595  {
29598  HeatmapsRedlowvaluesMenuItem->Caption = "Heatmaps: Set Red to Represent Low Values";
29599  Utilities->RedLowFlag = false;
29600  }
29601  else
29602  {
29605  HeatmapsRedlowvaluesMenuItem->Caption = "Heatmaps: Set Red to Represent High Values";
29606  Utilities->RedLowFlag = true;
29607  }
29608  Utilities->CallLogPop(2719);
29609 }
29610 
29611 //---------------------------------------------------------------------------
29612 
29614 {
29615  TrainController->LogEvent("TrainLongServRefInfoOnOffMenuItemClick");
29616  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",TrainLongServRefInfoOnOffMenuItemClick");
29618  {
29620  TrainLongServRefInfoOnOffMenuItem->Caption = "Show &Long Service References";
29622  ClearandRebuildRailway(3); //add this after trains replotted to tidy up any remaining text overwriting
29623  }
29624  else
29625  {
29627  TrainLongServRefInfoOnOffMenuItem->Caption = "Hide &Long Service References";
29630  }
29631  Utilities->CallLogPop(2730);
29632 }
29633 
29634 //---------------------------------------------------------------------------
29635 
29636 /*
29637  Problems with ifstream reading (see 'SessionFileIntegrityCheck(AnsiString FileName)' above):-
29638 
29639  These problems were with Borland C++Builder 4.
29640 
29641  The functions saved in OldFiles\Backups220809Duringifstream testing were used for testing the odd behaviour where the
29642  ifstream pointer gave different characters using get() and getline(), when reading the timetable entries in the session
29643  file for 20/08/09 at 18:45 (saved). All was well until point 48677 in the session file, when for some reason the
29644  getline(0 & get(0 gave different results. Many earlier timetable strings had been read OK before that, and it wasn't
29645  clear what was special about this particular string.
29646  Later more detailed study found that on reading the string beginning at point 48605 (i.e. the one earlier than above),
29647  within function CheckNoNewLineAtStartNonZeroTerminatedFileString the file pointer (using tellg()) reduced from 48606 to
29648  48604 after reading the 'F' character. Thereafter characters were read correctly but the pointer remained 2 too low.
29649  This is thought to be a flaw in the compiler.
29650  Later again additional tellg()s and a seekg()s were included in CheckNoNewLineAtStartNonZeroTerminatedFileString, and
29651  though these should have had no effect they somehow caused the next getline() within CheckTimetableFromSessionFile to
29652  read a null, even though the pointer had been reset to its value before the call to
29653  CheckNoNewLineAtStartNonZeroTerminatedFileString. Again this seems to be a flaw in the compiler, where the pointer
29654  that is indicated by tellg and the true pointer within the system can be different.
29655  Tried the old c++ stream library to see if that worked but it was exactly the same. Probably because the same code is
29656  used for both with the new library just defined within the std namespace.
29657  Success!! Traced to the putback function failing. It (apparently) can't be used if the file pointer has been altered
29658  after the last read that is to be put back. Corrected that & the most recent session file checked out & loaded OK.
29659  (note - don't need the ifstream file to be open in output mode for the putback to work)
29660  But: the earlier file - 18:45 as above - still fails to advance the file pointer in the middle of checking the
29661  timetable, it sticks at position 48601. This position points to 'r' in 'Frh' just before a newline. Also the file
29662  integrity is OK up to and after this sticking point. Oddly though the loading function works fine (i.e. by bypassing
29663  the integrity check function), though the timetable isn't read directly, it is copied to a new stand-alone timetable
29664  file and that read by the program.
29665  Created a new version of CheckNoNewLineAtStartNonZeroTerminatedFileString with 'New' at end, & got rid of all the
29666  internal digressions & getlines. This passed the earlier sticking point, but stuck later at 48677, i.e. the 'h' from
29667  'Frh' at the end of the entry following that for the earlier sticking point. Here
29668  CheckNoNewLineAtStartNonZeroTerminatedFileStringNew works fine, with end pointer correctly set at 48680, i.e. after the
29669  newline, but the subsequent getline() function, although it retrieves the line correctly, the file pointer is set to
29670  48677, i.e. before the newline, so getline seems to fail to extract the newline character. Still to check - why doesn't
29671  CheckNoNewLineAtStartNonZeroTerminatedFileStringNew see 'h' instead of '0' in the subsequent read? If it did the two
29672  would tally, though would still be wrong.
29673  Further investigation:- CheckNoNewLineAtStartNonZeroTerminatedFileStringNew doesn't seem to recognize the file pointer
29674  as set by seekg at 48677. It continues to read at the point it left off earlier, whereas getline() does read at 48677
29675  & recovers 'h'. Continuing to apply getline() after the above effect it is found that it doesn't extract newlines after
29676  reading further lines, but extracts them when read alone i.e. it reads a line then a null in succession, although the
29677  lines are only separated by single newline characters.
29678 
29679  Need to check:
29680  1. Does the file read correctly if only get() functions used without getline() and without resetting the file pointer?
29681  2. Does the file read correctly if only getline() functions used without get() and without resetting the file pointer?
29682  3. Does the file read correctly if get() functions alternated with getline() but without resetting the file pointer?
29683 
29684  For 1: Still goes wrong at usual place, reads 'h' at the same point. Try not resetting the file pointer with seekg.
29685  Tried this - got past the earlier point but failed later with a reduction in file pointer after a character read. In
29686  fact the reduction was by 40 bytes for reading a single comma! Try without any tellg's - yes, that got past all the
29687  timetable OK. So, works OK with just get() providing no tellg's (& no seekg's).
29688 
29689  For 2: Works OK using getline().
29690 
29691  For 3: Gets to end of timetable OK but the next tellg gives a wrong value. Check if using getline() alone gives a
29692  wrong tellg. Tried getline() alone, reached end of TT as before, but gave the same wrong file pos on using tellg.
29693  Try continuing to see if works OK in spite of tellg giving wrong result. Yes it works OK. Hence the problem seems to
29694  be tellg, which sometimes returns wrong results, and they corrupt things when used in seekg.
29695 
29696  Overall conclusion: Avoid all tellg's & seekg's. If need to reset a file position then close and reopen it.
29697 */
29698 
29699 //---------------------------------------------------------------------------
29700 
TInterface::NumToRly
bool NumToRly(unsigned char RlyUserNumber, AnsiString &RailwayName)
Definition: InterfaceUnit.cpp:28969
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19923
TInterface::SpeedButton59
TSpeedButton * SpeedButton59
Definition: InterfaceUnit.h:595
TInterface::CopyTTEntryButtonClick
void __fastcall CopyTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4030
TInterface::CtrlKey
bool CtrlKey
true when the CTRL key is pressed (see also ShiftKey)
Definition: InterfaceUnit.h:1290
TInterface::MTBFEditBox
TEdit * MTBFEditBox
Definition: InterfaceUnit.h:513
TOnePrefDir::LastElementPtr
TPrefDirVectorIterator LastElementPtr(int Caller)
Return a pointer to the last element in the vector.
Definition: TrackUnit.cpp:12532
TInterface::BecomeNewServiceMenuItemClick
void __fastcall BecomeNewServiceMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14885
TInterface::ScreenLeftButtonClick
void __fastcall ScreenLeftButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11244
TInterface::ExitMenuItem
TMenuItem * ExitMenuItem
Definition: InterfaceUnit.h:451
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:393
TInterface::TextMoveVPos
int TextMoveVPos
used to store the original text 'H' & 'V' positions for use during text moving
Definition: InterfaceUnit.h:1485
TInterface::OperMode
@ OperMode
Definition: InterfaceUnit.h:1016
TInterface::CPDirectionsCheckBox
TCheckBox * CPDirectionsCheckBox
Definition: InterfaceUnit.h:692
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:359
TInterface::SpeedButton140
TSpeedButton * SpeedButton140
Definition: InterfaceUnit.h:676
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:358
TUserGraphicItem::UserGraphic
TPicture * UserGraphic
Definition: DisplayUnit.h:37
TInterface::TDynamicMapComp::operator()
bool operator()(const TNumHVPair &lower, const TNumHVPair &higher) const
< Dynamic map comparator based on Num then H & V
Definition: InterfaceUnit.cpp:27525
TPrefDirElement::SetEXGraphicPtr
void SetEXGraphicPtr(Graphics::TBitmap *input)
Used in pasting pref dirs.
Definition: TrackUnit.h:370
TTrack::UserGraphicVectorAt
TUserGraphicItem & UserGraphicVectorAt(int Caller, int At)
A range-checked version of UserGraphicVector.at(At)
Definition: TrackUnit.cpp:12505
TTrack::ResetTSRs
void ResetTSRs(int Caller)
Called on exit from operation to reset failed to false for all simple track elements & clear TSRVecto...
Definition: TrackUnit.cpp:4825
TInterface::AddPrefDirButton
TBitBtn * AddPrefDirButton
Definition: InterfaceUnit.h:156
TInterface::RemoveTrainMenuItem
TMenuItem * RemoveTrainMenuItem
Definition: InterfaceUnit.h:500
TInterface::LocationNamesSetImage
TImage * LocationNamesSetImage
Definition: InterfaceUnit.h:326
TInterface::SpeedLimitBox
TEdit * SpeedLimitBox
distance/speed setting edit box that accepts speed limits
Definition: InterfaceUnit.h:132
TTextItem::VPos
int VPos
the vertical position on the railway
Definition: TextUnit.h:51
TInterface::SaveImageAndGridMenuItemClick
void __fastcall SaveImageAndGridMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2983
TDisplay::DisplayOffsetHHome
static int DisplayOffsetHHome
the horizontal offset of the 'Home' display
Definition: DisplayUnit.h:81
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:750
TInterface::FloatingInfoMenu
TMenuItem * FloatingInfoMenu
Definition: InterfaceUnit.h:478
TInterface::SpeedButton34
TSpeedButton * SpeedButton34
Definition: InterfaceUnit.h:570
TInterface::NewTTEntryKeyFlag
bool NewTTEntryKeyFlag
Definition: InterfaceUnit.h:1408
TInterface::ConflictAnalysisButtonClick
void __fastcall ConflictAnalysisButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17478
TInterface::CallingOnButton
TSpeedButton * CallingOnButton
Definition: InterfaceUnit.h:245
TInterface::BuildTrainDataVectorForValidateFile
bool BuildTrainDataVectorForValidateFile(int Caller, std::ifstream &TTBLFile, bool GiveMessages, bool CheckLocationsExistInRailway)
Check the integrity of a stored timetable file (either as a stand alone file or within a session file...
Definition: InterfaceUnit.cpp:24414
TInterface::SpeedButton42
TSpeedButton * SpeedButton42
Definition: InterfaceUnit.h:578
TInterface::SelectBitmapMouseLocX
int SelectBitmapMouseLocX
when flag SelectPickedUp is set to true (see above - to allow a selected screen area to move during M...
Definition: InterfaceUnit.h:1458
TInterface::PrefDirPanel
TPanel * PrefDirPanel
'Set preferred directions' panel
Definition: InterfaceUnit.h:402
TInterface::PrefDirPanelLabel
TLabel * PrefDirPanelLabel
label to the left of PrefDirPanel
Definition: InterfaceUnit.h:351
FMinor
@ FMinor
Definition: Utilities.h:43
TOnePrefDir::RebuildPrefDirVector
void RebuildPrefDirVector(int Caller)
Called after the track vector has been rebuilt following linking, to rebuild the preferred direction ...
Definition: TrackUnit.cpp:14050
TInterface::MTBFEditBoxKeyUp
void __fastcall MTBFEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:17269
TInterface::TrackMode
@ TrackMode
Definition: InterfaceUnit.h:1016
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1746
TInterface::SaveSessionButton
TBitBtn * SaveSessionButton
Definition: InterfaceUnit.h:241
TInterface::TTLabel2
TLabel * TTLabel2
Definition: InterfaceUnit.h:383
TInterface::ElapsedTimerRunning
bool ElapsedTimerRunning
Definition: InterfaceUnit.h:1239
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:182
TInterface::TTClockx1ButtonClick
void __fastcall TTClockx1ButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16895
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:214
TInterface::ExpandRepeatsButtonClick
void __fastcall ExpandRepeatsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5472
TInterface::LengthConversionPanel
TPanel * LengthConversionPanel
Definition: InterfaceUnit.h:145
TInterface::SpeedButton143
TSpeedButton * SpeedButton143
Definition: InterfaceUnit.h:679
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6662
TInterface::ChangeDirectionMenuItem
TMenuItem * ChangeDirectionMenuItem
Definition: InterfaceUnit.h:494
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:381
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:22563
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:860
TInterface::StepForwardMenuItem
TMenuItem * StepForwardMenuItem
Definition: InterfaceUnit.h:497
TTrack::SelectGraphicVector
TUserGraphicVector SelectGraphicVector
vectors of user graphics
Definition: TrackUnit.h:823
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:92
TInterface::DelayMenu
TMenuItem * DelayMenu
Definition: InterfaceUnit.h:745
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:56
TInterface::SignallerControlStopMenuItem
TMenuItem * SignallerControlStopMenuItem
Definition: InterfaceUnit.h:499
TUtilities::MinorPointChangeEventsPerFailure
int MinorPointChangeEventsPerFailure
Definition: Utilities.h:60
TRailGraphics::ConvertSignalsToOppositeHand
void ConvertSignalsToOppositeHand(int Caller)
Converts all signal graphics to opposite hand new at v2.3.0.
Definition: GraphicUnit.cpp:4737
TInterface::FormResize
void __fastcall FormResize(TObject *Sender)
Definition: InterfaceUnit.cpp:17167
TInterface::SpeedButton70
TSpeedButton * SpeedButton70
Definition: InterfaceUnit.h:606
TInterface::SpeedButton144
TSpeedButton * SpeedButton144
Definition: InterfaceUnit.h:680
TInterface::SpeedButton32
TSpeedButton * SpeedButton32
Definition: InterfaceUnit.h:568
TInterface::SaveTTAsButtonClick
void __fastcall SaveTTAsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4953
TInterface::MPCPLabel2
TLabel * MPCPLabel2
Definition: InterfaceUnit.h:715
TInterface::SelectBitmapHLoc
int SelectBitmapHLoc
the original (prior to moving & after finished moving) HLoc value of Edit->Select & Edit->Reselect
Definition: InterfaceUnit.h:1450
TInterface::OperatorActionButton
TBitBtn * OperatorActionButton
Definition: InterfaceUnit.h:242
TInterface::DistanceKey
TImage * DistanceKey
Definition: InterfaceUnit.h:310
AboutUnit.h
Arrive
@ Arrive
Definition: TrainUnit.h:55
TInterface::SpeedButton54
TSpeedButton * SpeedButton54
Definition: InterfaceUnit.h:590
TInterface::PasteWarningSentFlag
bool PasteWarningSentFlag
indicates that the warning message about pasting overwriting the area has been given,...
Definition: InterfaceUnit.h:1324
TInterface::StartWholeRailwayMoveVPos
int StartWholeRailwayMoveVPos
mouse Y position when start to move the whole railway
Definition: InterfaceUnit.h:1471
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:357
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:399
TInterface::UnrestrictedButton
TBitBtn * UnrestrictedButton
Definition: InterfaceUnit.h:237
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:13087
TTrack::UserGraphicMap
TUserGraphicMap UserGraphicMap
the map of graphic filenames as key and TPicture* as values
Definition: TrackUnit.h:825
TRailGraphics::SpeedBut72NormBlackGlyph
Graphics::TBitmap * SpeedBut72NormBlackGlyph
Definition: GraphicUnit.h:1089
TOnePrefDir::CheckPrefDirAgainstTrackVector
void CheckPrefDirAgainstTrackVector(int Caller)
Check loaded PrefDir against loaded track, and if discrepancies found give message & clear EveryPrefD...
Definition: TrackUnit.cpp:14090
TInterface::ExportTTMenuItemClick
void __fastcall ExportTTMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3376
TInterface::RestoreTTKeyFlag
bool RestoreTTKeyFlag
Definition: InterfaceUnit.h:1415
TInterface::CancelSelectionMenuItemClick
void __fastcall CancelSelectionMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12946
TRailGraphics::ChangeAllTransparentColours
void ChangeAllTransparentColours(TColor NewTransparentColour, TColor OldTransparentColour)
Uses 'ChangeTransparentColour' method to change each graphic in turn.
Definition: GraphicUnit.cpp:3974
TInterface::PERFLOG_DIR_NAME
static const UnicodeString PERFLOG_DIR_NAME
Definition: InterfaceUnit.h:1047
TInterface::MPHPOwnPortEditBoxKeyUp
void __fastcall MPHPOwnPortEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:27432
TInterface::SpeedHeatMapImageRedLow
TImage * SpeedHeatMapImageRedLow
Definition: InterfaceUnit.h:315
TInterface::WholeRailwayMoving
bool WholeRailwayMoving
true when moving the railway with the mouse, new at v2.1.0
Definition: InterfaceUnit.h:1392
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
TInterface::TDynamicMapEntry
std::pair< TNumHVPair, TServiceInfo > TDynamicMapEntry
Definition: InterfaceUnit.h:1134
TInterface::TextFoundFlag
bool TextFoundFlag
indicates that a text item has been found when clicking on a build screen during 'AddText' or 'MoveTe...
Definition: InterfaceUnit.h:1358
TInterface::TrainStatusShowing
bool TrainStatusShowing
Definition: InterfaceUnit.h:1171
TInterface::DisableRouteButtons
void DisableRouteButtons(int Caller)
Called during operation whenever the route type buttons need to be disabled, e.g. when paused.
Definition: InterfaceUnit.cpp:25274
TInterface::OperateRailwayMenuItemClick
void __fastcall OperateRailwayMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2218
TInterface::TCMIterator
TCouplingMap::iterator TCMIterator
Definition: InterfaceUnit.h:1147
TInterface::DerailImage
TImage * DerailImage
Definition: InterfaceUnit.h:306
TRailGraphics::CouplingExit2
Graphics::TBitmap * CouplingExit2
Definition: GraphicUnit.h:557
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:445
TInterface::TTLabel3
TLabel * TTLabel3
Definition: InterfaceUnit.h:384
TInterface::ShowReminderListBox
void ShowReminderListBox(int Caller)
makes ReminderListBox visible
Definition: InterfaceUnit.cpp:14804
TRailGraphics::smLC
Graphics::TBitmap * smLC
Definition: GraphicUnit.h:900
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:805
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:287
TRailGraphics::SpeedBut75NormBlackGlyph
Graphics::TBitmap * SpeedBut75NormBlackGlyph
Definition: GraphicUnit.h:1092
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7512
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:56
TTrainController::LastSessionSaveTTClockTime
TDateTime LastSessionSaveTTClockTime
Definition: TrainUnit.h:761
TInterface::TTSelectedEntry
AnsiString TTSelectedEntry
used to record the current timetable entry when changing to AZ order, Time order or back to original ...
Definition: InterfaceUnit.h:1268
TAllRoutes::SearchAllRoutesAndTruncate
bool SearchAllRoutesAndTruncate(int Caller, int HLoc, int VLoc, bool PrefDirRoute)
Examines all routes and for each uses TruncateRoute to see if the element at H & V is present in that...
Definition: TrackUnit.cpp:19885
TInterface::HostMultiplayInProgressFlag
bool HostMultiplayInProgressFlag
Definition: InterfaceUnit.h:1197
TUtilities::ScreenElementWidth
int ScreenElementWidth
width of display screen in elements
Definition: Utilities.h:103
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1675
TInterface::SpeedButton19
TSpeedButton * SpeedButton19
Definition: InterfaceUnit.h:555
TInterface::HeatmapsRedlowvaluesMenuItemClick
void __fastcall HeatmapsRedlowvaluesMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:29590
TTrack::GetVectorPositionFromTrackMap
int GetVectorPositionFromTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Returns the track vector position corresponding to the Hloc & VLoc positions, FoundFlag indicates whe...
Definition: TrackUnit.cpp:5852
TTrack::PlotLCBaseElementsOnly
void PlotLCBaseElementsOnly(int Caller, TBarrierState State, int BaseElementSpeedTag, int HLoc, int VLoc, int TypeOfRoute, TDisplay *Disp)
Just replot the basic track elements at a level crossing (for flashing)
Definition: TrackUnit.cpp:7413
TTrack::GetTrackElementFromTrackMap
TTrackElement & GetTrackElementFromTrackMap(int Caller, int HLoc, int VLoc)
Return a reference to the element at HLoc & VLoc, if no element is found an error is thrown.
Definition: TrackUnit.cpp:5879
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:145
TInterface::MasterClockTimer
void __fastcall MasterClockTimer(TObject *Sender)
Definition: InterfaceUnit.cpp:10158
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:787
TInterface::RightClickTrainMousePosX
int RightClickTrainMousePosX
Definition: InterfaceUnit.h:1466
TInterface::MainScreen
TImage * MainScreen
the railway display screen
Definition: InterfaceUnit.h:333
TInterface::SpeedButton67
TSpeedButton * SpeedButton67
Definition: InterfaceUnit.h:603
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:67
TInterface::CutMenuItem
TMenuItem * CutMenuItem
Definition: InterfaceUnit.h:466
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:70
TInterface::SpeedButton120
TSpeedButton * SpeedButton120
Definition: InterfaceUnit.h:656
TInterface::LCResetCounter
unsigned int LCResetCounter
count up to 20 then resets - to check LCs & raise barriers if no route & no train present
Definition: InterfaceUnit.h:1434
TInterface::BlueBgndMenuItem
TMenuItem * BlueBgndMenuItem
Definition: InterfaceUnit.h:461
TInterface::SetReminderMenuItemClick
void __fastcall SetReminderMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14337
TTrack::NoActiveTrack
bool NoActiveTrack(int Caller)
True if there is no active track in the railway.
Definition: TrackUnit.cpp:1978
TInterface::TTLabel1
TLabel * TTLabel1
Definition: InterfaceUnit.h:382
TInterface::FirstActionsDueFormDisplay
bool FirstActionsDueFormDisplay
places ActionsDueForm at bottom RHS when first displayed
Definition: InterfaceUnit.h:1032
TTrack::CalcHLocMinEtc
void CalcHLocMinEtc(int Caller)
Examine TrackVector, InactiveTrackVector and TextVector, and set the values that indicate the extent ...
Definition: TrackUnit.cpp:11035
TInterface::ModerateDelaysMenuItemClick
void __fastcall ModerateDelaysMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27005
TInterface::ResetChangedFileDataAndCaption
void ResetChangedFileDataAndCaption(int Caller, bool NonPrefDirChangesMade)
Called whenever the railway is changed to deal with title displays (loaded railway and timetable) and...
Definition: InterfaceUnit.cpp:22279
TRailGraphics::CouplingExit3
Graphics::TBitmap * CouplingExit3
Definition: GraphicUnit.h:558
TTrain::CoastingBrakeRate
double CoastingBrakeRate
the train brake rate when coasting
Definition: TrainUnit.h:453
TInterface::ClpBrdValid
AnsiString ClpBrdValid
set to RlyClpBrdCopy or RlyClpBrd_Cut when Windows Clipboard contains a valid railway segment
Definition: InterfaceUnit.h:1244
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:449
TInterface::SpeedButton52
TSpeedButton * SpeedButton52
Definition: InterfaceUnit.h:588
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:75
TInterface::IMAGE_DIR_NAME
static const UnicodeString IMAGE_DIR_NAME
Definition: InterfaceUnit.h:1049
TInterface::LoadSessionDialog
TOpenDialog * LoadSessionDialog
Definition: InterfaceUnit.h:521
TInterface::LoadCouplingFileDialog
TOpenDialog * LoadCouplingFileDialog
Definition: InterfaceUnit.h:700
TTextHandler::TextVector
TTextVector TextVector
Definition: TextUnit.h:71
TInterface::OutputLog10MouseDown
void __fastcall OutputLog10MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16260
TTrack::Lowering
@ Lowering
Definition: TrackUnit.h:614
TTrack::FindNonPlatformMatch
bool FindNonPlatformMatch(int Caller, int HLoc, int VLoc, int &Position, TTrackElement &TrackElement)
True if find a non-platform element at HLoc & VLoc, and if so return its TrackVector position and a r...
Definition: TrackUnit.cpp:2886
TInterface::BecomeNewServiceMenuItem
TMenuItem * BecomeNewServiceMenuItem
Definition: InterfaceUnit.h:744
TInterface::SpeedButton116
TSpeedButton * SpeedButton116
Definition: InterfaceUnit.h:652
TInterface::ReloadConfigMenuItem
TMenuItem * ReloadConfigMenuItem
Definition: InterfaceUnit.h:737
TTrack::MatchingPoint
bool MatchingPoint(int Caller, unsigned int TrackVectorPosition, unsigned int DivergingPosition)
Definition: TrackUnit.cpp:6032
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:381
TInterface::NewSelectBitmapVLoc
int NewSelectBitmapVLoc
as above for VLoc
Definition: InterfaceUnit.h:1444
TInterface::MoveTextOrGraphic
@ MoveTextOrGraphic
Definition: InterfaceUnit.h:1065
TInterface::TEVIterator
TTEVIterator TEVIterator
Definition: InterfaceUnit.h:1529
FModerate
@ FModerate
Definition: Utilities.h:43
TInterface::ScreenGridButton
TBitBtn * ScreenGridButton
Definition: InterfaceUnit.h:110
TTrack::EnterLocationName
void EnterLocationName(int Caller, AnsiString LocationName, bool AddingElements)
All platform, concourse, footcrossing & non-station named location elements are able to have a Locati...
Definition: TrackUnit.cpp:8440
TInterface::EditMenu
TMenuItem * EditMenu
Definition: InterfaceUnit.h:463
TUtilities::RedLowFlag
bool RedLowFlag
Sets Red = low values for heatmaps.
Definition: Utilities.h:77
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:429
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:459
TUtilities::NilMTBTSRs
int NilMTBTSRs
Definition: Utilities.h:59
TTrack::TTrackVectorIterator
std::vector< TTrackElement >::iterator TTrackVectorIterator
iterator for TTrackVector
Definition: TrackUnit.h:652
TInterface::ResetSelectRect
void ResetSelectRect()
SelectRect is the rectangle selected via the 'Edit'menu, and this function sets left,...
Definition: InterfaceUnit.cpp:26209
TInterface::TTClockAdjustWarningHide
bool TTClockAdjustWarningHide
true if user opts not to show the timetable clock adjustment warning (false on starting the program)
Definition: InterfaceUnit.h:1366
TInterface::TTLabel9
TLabel * TTLabel9
Definition: InterfaceUnit.h:390
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:353
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:846
TInterface::TTClockAdjustCheckBox
TCheckBox * TTClockAdjustCheckBox
Definition: InterfaceUnit.h:269
TOnePrefDir::SavePrefDirVector
void SavePrefDirVector(int Caller, std::ofstream &VecFile)
Save the preferred direction vector to a file.
Definition: TrackUnit.cpp:13829
TInterface::TrackNotLinkedImage
TImage * TrackNotLinkedImage
Definition: InterfaceUnit.h:323
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:401
TInterface::WarningHover
bool WarningHover
true when mouse hovers over warning messages during operation - to prevent clicking while changing
Definition: InterfaceUnit.h:1390
TTrack::GetTrackLocsFromScreenPos
void GetTrackLocsFromScreenPos(int Caller, int &HLoc, int &VLoc, int ScreenPosH, int ScreenPosV)
Converse of GetScreenPositionsFromTruePos except that in this function HLoc & VLoc are expressed in t...
Definition: TrackUnit.cpp:7898
TInterface::SaveTTAsButton
TButton * SaveTTAsButton
Definition: InterfaceUnit.h:186
TInterface::ZoomButtonClick
void __fastcall ZoomButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11489
TInterface::SpeedButton90
TSpeedButton * SpeedButton90
Definition: InterfaceUnit.h:626
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:381
TTrackElement::StationEntryStopLinkPos3
int StationEntryStopLinkPos3
Definition: TrackUnit.h:153
TTrack::GetLocationName
AnsiString GetLocationName(unsigned int InactiveTrackVectorPosition)
Return location name for a given inactive track vector position.
Definition: TrackUnit.h:839
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:846
TInterface::ExitTrackButtonClick
void __fastcall ExitTrackButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1901
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:11060
TInterface::SpeedButton135
TSpeedButton * SpeedButton135
Definition: InterfaceUnit.h:671
TInterface::SpeedButton111
TSpeedButton * SpeedButton111
Definition: InterfaceUnit.h:647
TInterface::RouteCancelFlag
bool RouteCancelFlag
true when route cancel button pressed, enables a right mouse click to cancel a route if in an appropr...
Definition: InterfaceUnit.h:1338
TTextHandler::LoadText
void LoadText(int Caller, std::ifstream &VecFile)
load the railway's text from VecFile
Definition: TextUnit.cpp:300
TInterface::SpeedToggleButton2Click
void __fastcall SpeedToggleButton2Click(TObject *Sender)
Definition: InterfaceUnit.cpp:16413
TOnePrefDir::GetStartAndEndPrefDirElements
bool GetStartAndEndPrefDirElements(int Caller, TPrefDirElement &StartElement, TPrefDirElement &EndElement, int &LastIteratorValue)
Called when searching for start and end PrefDirElements when setting up automatic signals routes in P...
Definition: TrackUnit.cpp:15223
TTextItem::HPos
int HPos
the horizontal position on the railway
Definition: TextUnit.h:49
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:17770
TInterface::LoadNormalSignalGlyphs
void LoadNormalSignalGlyphs(int Caller)
In trackbuild display normal signal types on signal buttons.
Definition: InterfaceUnit.cpp:26307
TInterface::MPHPOwnIPEditBoxKeyUp
void __fastcall MPHPOwnIPEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:27381
TInterface::SkipEventsBeforeSameLoc
void SkipEventsBeforeSameLoc(int Caller, int TrainID, AnsiString LocationName)
used when change early to the next service (Fns, Fns-sh, Frh-sh or F-nshs) to advance the action poin...
Definition: InterfaceUnit.cpp:15125
TInterface::MainScreenMouseDown3
void MainScreenMouseDown3(int Caller, TMouseButton Button, TShiftState Shift, int X, int Y)
Called when mouse button clicked in zoom-out mode.
Definition: InterfaceUnit.cpp:9314
TInterface::TTLabel11
TLabel * TTLabel11
Definition: InterfaceUnit.h:391
TInterface::CancelTTEntryButton
TButton * CancelTTEntryButton
Definition: InterfaceUnit.h:177
TInterface::SpeedButton8
TSpeedButton * SpeedButton8
Definition: InterfaceUnit.h:544
TInterface::UserGraphicVectorNumber
int UserGraphicVectorNumber
used to store a single item of user graphics
Definition: InterfaceUnit.h:1487
TInterface::PreviousTTEntryButton
TButton * PreviousTTEntryButton
Definition: InterfaceUnit.h:168
TInterface::TrackElementPanel
TPanel * TrackElementPanel
panel containing the track/location/parapet element buttons
Definition: InterfaceUnit.h:417
TInterface::MinorDelaysMenuItemClick
void __fastcall MinorDelaysMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:26993
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:20718
TInterface::SelectionValid
bool SelectionValid
true when an area of screen has been selected via the 'Edit' & 'Select' or 'Reselect' menu items
Definition: InterfaceUnit.h:1344
TDisplay::InvertElement
void InvertElement(int Caller, int HPos, int VPos)
Definition: DisplayUnit.cpp:144
TInterface::TRlyUserInfo::UserName
AnsiString UserName
Definition: InterfaceUnit.h:1106
TInterface::OperateButtonClick
void __fastcall OperateButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2237
TInterface::WhiteBgndMenuItem
TMenuItem * WhiteBgndMenuItem
Definition: InterfaceUnit.h:459
TInterface::ContinuationAutoSignals
void ContinuationAutoSignals(int Caller, TDateTime Now)
Deal with signal resetting on auto signal routes that extend to continuations where trains have depar...
Definition: InterfaceUnit.cpp:20182
TInterface::TTLastServiceIterator
TTEVIterator TTLastServiceIterator
timetable entry value pointers used during timetable editing
Definition: InterfaceUnit.h:1529
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:846
TInterface::LoadInterface
void LoadInterface(int Caller, std::ifstream &SessionFile)
Load the interface part of a session file.
Definition: InterfaceUnit.cpp:23675
TInterface::SpeedButton2
TSpeedButton * SpeedButton2
Definition: InterfaceUnit.h:538
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9710
TInterface::AutoSigsFlag
bool AutoSigsFlag
true when AutoSig route building selected during operation
Definition: InterfaceUnit.h:1274
TInterface::SetRouteButtonsInfoCaptionAndRouteNotStarted
void SetRouteButtonsInfoCaptionAndRouteNotStarted(int Caller)
Enables or disables the route type buttons depending on the route mode, sets the information panel me...
Definition: InterfaceUnit.cpp:25124
TInterface::MoveForwardsMenuItem
TMenuItem * MoveForwardsMenuItem
Definition: InterfaceUnit.h:495
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:139
TTrackElement::FourAspect
@ FourAspect
Definition: TrackUnit.h:161
TInterface::YardEdit
TEdit * YardEdit
Definition: InterfaceUnit.h:140
TInterface::SkipListHeaderPanel
TPanel * SkipListHeaderPanel
Definition: InterfaceUnit.h:740
TInterface::SigPrefConsecButtonClick
void __fastcall SigPrefConsecButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2344
TTrack::CheckActiveLCVector
bool CheckActiveLCVector(int Caller, std::ifstream &VecFile)
Definition: TrackUnit.cpp:3724
TTrack::FindSetAndDisplayMatchingGap
bool FindSetAndDisplayMatchingGap(int Caller, int HLoc, int VLoc)
True if find an unset gap that matches the gap at HLoc & VLoc, if find one mark it with a green circl...
Definition: TrackUnit.cpp:4575
TInterface::Paused
@ Paused
Definition: InterfaceUnit.h:1055
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:17785
TInterface::SpeedButton41
TSpeedButton * SpeedButton41
Definition: InterfaceUnit.h:577
TRailGraphics::SpeedBut73NormBlackGlyph
Graphics::TBitmap * SpeedBut73NormBlackGlyph
Definition: GraphicUnit.h:1090
TInterface::Operating
@ Operating
Definition: InterfaceUnit.h:1055
TTextHandler
A single object that handles text management.
Definition: TextUnit.h:64
TInterface::EditTimetableMenuItemClick
void __fastcall EditTimetableMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3537
TInterface::TakeSignallerControlMenuItemClick
void __fastcall TakeSignallerControlMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:13695
TInterface::TimetableMode
@ TimetableMode
Definition: InterfaceUnit.h:1016
TInterface::ScreenUpButtonClick
void __fastcall ScreenUpButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11428
TInterface::ShowHideTTButtonClick
void __fastcall ShowHideTTButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3730
TInterface::ImageMenu
TMenuItem * ImageMenu
Definition: InterfaceUnit.h:482
TInterface::LoadGroundSignalGlyphs
void LoadGroundSignalGlyphs(int Caller)
In trackbuild display ground signal types on signal buttons.
Definition: InterfaceUnit.cpp:26323
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:196
TInterface::ReloadConfigMenuItemClick
void __fastcall ReloadConfigMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17555
TInterface::CPArrivalsCheckBox
TCheckBox * CPArrivalsCheckBox
Definition: InterfaceUnit.h:282
TInterface::OneEntryTimetableMemo
TMemo * OneEntryTimetableMemo
timetable help text displayed on the timetable edit screen
Definition: InterfaceUnit.h:437
TInterface::InvertTTEntryMessageSent
bool InvertTTEntryMessageSent
indicates that the invert entry timetable message won't be given again
Definition: InterfaceUnit.h:1304
TInterface::MajorFailuresMenuItem
TMenuItem * MajorFailuresMenuItem
Definition: InterfaceUnit.h:754
TInterface::SelectBitmapVLoc
int SelectBitmapVLoc
the original (prior to moving & after finished moving) VLoc value of Edit->Select & Edit->Reselect
Definition: InterfaceUnit.h:1462
TInterface::LengthOKButtonClick
void __fastcall LengthOKButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1529
session_api_
API * session_api_
Definition: InterfaceUnit.cpp:72
TInterface::SpeedButton47
TSpeedButton * SpeedButton47
Definition: InterfaceUnit.h:583
TInterface::OutputLog1MouseDown
void __fastcall OutputLog1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16172
NotStarted
@ NotStarted
Definition: TrainUnit.h:92
TTrack::ShowSelectedGap
void ShowSelectedGap(int Caller, TDisplay *Disp)
Called during gap setting to mark a gap with a red circle - after which the program awaits user selec...
Definition: TrackUnit.cpp:4735
TInterface::AppDeactivate
void __fastcall AppDeactivate(TObject *Sender)
Definition: InterfaceUnit.cpp:783
TInterface::UnrestrictedButtonClick
void __fastcall UnrestrictedButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2425
TInterface::ExportTTMenuItem
TMenuItem * ExportTTMenuItem
Definition: InterfaceUnit.h:449
TTrack::PlotLoweredLinkedLevelCrossingBarriers
void PlotLoweredLinkedLevelCrossingBarriers(int Caller, int BaseElementSpeedTag, int HLoc, int VLoc, int TypeOfRoute, TDisplay *Disp, bool Manual)
Plot & open (to trains) all level crossings linked to TrackElement (Manual true = manually lowered,...
Definition: TrackUnit.cpp:6696
TInterface::SaveTTKeyFlag
bool SaveTTKeyFlag
Definition: InterfaceUnit.h:1413
TRailGraphics::SpeedBut71NormBlackGlyph
Graphics::TBitmap * SpeedBut71NormBlackGlyph
Definition: GraphicUnit.h:1088
TInterface::SelectNewGraphicClick
void __fastcall SelectNewGraphicClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17416
TInterface::SpeedButtonClick
void __fastcall SpeedButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:917
TTrack::GetHLocMax
int GetHLocMax()
Definition: TrackUnit.h:890
TTrack::CheckUserGraphics
bool CheckUserGraphics(int Caller, std::ifstream &InFile, UnicodeString GraphicsPath)
checks all user graphics & returns true for success
Definition: TrackUnit.cpp:3600
TInterface::CPEditArrRange
TEdit * CPEditArrRange
Definition: InterfaceUnit.h:285
TInterface::ErrorButtonClick
void __fastcall ErrorButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:15555
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:900
TInterface::PasteTTEntryButton
TButton * PasteTTEntryButton
Definition: InterfaceUnit.h:172
TInterface::SpeedButton139
TSpeedButton * SpeedButton139
Definition: InterfaceUnit.h:675
TInterface::SpeedButton3
TSpeedButton * SpeedButton3
Definition: InterfaceUnit.h:539
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:360
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TOnePrefDir::TPrefDir4MultiMapIterator
std::multimap< THVPair, unsigned int, TMapComp >::iterator TPrefDir4MultiMapIterator
Definition: TrackUnit.h:1343
TInterface::SetLevel2PrefDirMode
void SetLevel2PrefDirMode(int Caller)
Sets the Level2PrefDirMode user mode, using the Level2PrefDirMode variable to determine the mode.
Definition: InterfaceUnit.cpp:19850
TInterface::SetGapsButton
TBitBtn * SetGapsButton
Definition: InterfaceUnit.h:102
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:387
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:215
TPrefDirElement::SetXLinkPos
void SetXLinkPos(int input)
Used in pasting pref dirs.
Definition: TrackUnit.h:358
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:300
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:890
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:21203
TInterface::ExitMenuItemClick
void __fastcall ExitMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:7351
TrackUnit.h
TInterface::SpeedButton29
TSpeedButton * SpeedButton29
Definition: InterfaceUnit.h:565
TUtilities::MajorMTBTSRs
int MajorMTBTSRs
Definition: Utilities.h:68
TInterface::SpeedButton35
TSpeedButton * SpeedButton35
Definition: InterfaceUnit.h:571
TInterface::ScreenDownButtonClick
void __fastcall ScreenDownButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11366
TInterface::api_oper_mode_
int api_oper_mode_
Definition: InterfaceUnit.h:1231
TAllRoutes::CheckRoutes
bool CheckRoutes(int Caller, int NumberOfActiveElements, std::ifstream &InFile)
Performs an integrity check on the routes stored in a session file and returns false if there is an e...
Definition: TrackUnit.cpp:21400
TInterface::RlyToNum
bool RlyToNum(AnsiString RailwayName, unsigned char &RlyUserNumber)
Definition: InterfaceUnit.cpp:28929
TDisplay::ClearDisplay
void ClearDisplay(int Caller)
Empty the display.
Definition: DisplayUnit.cpp:185
TDisplay::Top
int Top()
Return the top pixel position of the screen.
Definition: DisplayUnit.h:121
TInterface::ExitPrefDirButtonClick
void __fastcall ExitPrefDirButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2199
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:22734
TInterface::SaveErrorFile
void SaveErrorFile()
Save the error log after an error has been thrown - no need for a caller.
Definition: InterfaceUnit.cpp:25288
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6838
TInterface::SpeedVariableLabel2
TLabel * SpeedVariableLabel2
Definition: InterfaceUnit.h:153
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1562
TAllRoutes
Handles data and functions relating to all routes on the railway.
Definition: TrackUnit.h:1654
TInterface::TrainStatusInfoOnOffMenuItemClick
void __fastcall TrainStatusInfoOnOffMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:7419
TInterface::SpeedButton96
TSpeedButton * SpeedButton96
Definition: InterfaceUnit.h:632
TInterface::TTClockxHalfButtonClick
void __fastcall TTClockxHalfButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16914
TInterface::OutputLog3
TLabel * OutputLog3
Definition: InterfaceUnit.h:342
TTextHandler::GetFontStyleAsInt
int GetFontStyleAsInt(int Caller, TFont *InputFont)
retrieve the style of the font as a coded integer
Definition: TextUnit.cpp:102
TUserGraphicItem::VPos
int VPos
Definition: DisplayUnit.h:35
TOnePrefDir::ExternalStorePrefDirElement
void ExternalStorePrefDirElement(int Caller, TPrefDirElement LoadPrefDirElement)
Store a single pref dir element in the vector & map - used by other classes.
Definition: TrackUnit.h:1475
clB5G5R4
#define clB5G5R4
Definition: GraphicUnit.h:286
Utilities.h
TInterface::SetTrackModeEditMenu
void SetTrackModeEditMenu(int Caller)
Enables or disables the initial Edit mode submenu items in Track mode.
Definition: InterfaceUnit.cpp:26077
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:780
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:341
TInterface::SaveImageNoGridMenuItemClick
void __fastcall SaveImageNoGridMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2898
TInterface::SpeedButton25
TSpeedButton * SpeedButton25
Definition: InterfaceUnit.h:561
TInterface::LengthHeatmapBitBtn
TBitBtn * LengthHeatmapBitBtn
Definition: InterfaceUnit.h:121
TInterface::HelpMenu
TMenuItem * HelpMenu
Definition: InterfaceUnit.h:488
TTrack::SimpleVector
TSimpleVector SimpleVector
vector of simple element track vector positions
Definition: TrackUnit.h:821
TInterface::TrackOKButtonClick
void __fastcall TrackOKButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1078
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:17888
TTrack::SaveTrack
void SaveTrack(int Caller, std::ofstream &VecFile, bool GraphicsFollow)
Save all active and inactive track elements to VecFile.
Definition: TrackUnit.cpp:3324
TInterface::NoDelaysMenuItemClick
void __fastcall NoDelaysMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:26981
TInterface::GapsSetImage
TImage * GapsSetImage
Definition: InterfaceUnit.h:324
TOneRoute::GetNonPreferredRouteStartElement
bool GetNonPreferredRouteStartElement(int Caller, int HLoc, int VLoc, bool Callon)
Set the starting conditions for a non-preferred (i.e. unrestricted) route selection beginning on HLoc...
Definition: TrackUnit.cpp:16985
API::find_metadata_file
void find_metadata_file()
tries to find a metadata file for the currently session
Definition: API.cpp:44
TUserGraphicItem::HPos
int HPos
Definition: DisplayUnit.h:35
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TInterface::LocationNameComboBoxKeyUp
void __fastcall LocationNameComboBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:6394
TInterface::ClipboardChecked
bool ClipboardChecked
used to prevent Windows clipboard being checked repeatedly
Definition: InterfaceUnit.h:1282
TInterface::RevertToOriginalRouteSelector
void RevertToOriginalRouteSelector(int Caller)
Clears any route start markers, enables or disables the route cancel button, and resets the informati...
Definition: InterfaceUnit.cpp:18438
TInterface::FileIntegrityCheck
bool FileIntegrityCheck(int Caller, char *FileName) const
Check integrity of a railway file prior to loading, return true for success.
Definition: InterfaceUnit.cpp:18275
TInterface::OwnRlyUserNumber
unsigned char OwnRlyUserNumber
Definition: InterfaceUnit.h:1208
TUtilities::OverrideAndHideSignalBridgeMessage
bool OverrideAndHideSignalBridgeMessage
< used to send no platforms warning once only
Definition: Utilities.h:87
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:17826
TInterface::CouplingFileLoadedFlag
bool CouplingFileLoadedFlag
Definition: InterfaceUnit.h:1198
TInterface::SpeedButton126
TSpeedButton * SpeedButton126
Definition: InterfaceUnit.h:662
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:55
TInterface::PlayerMultiplayInProgressFlag
bool PlayerMultiplayInProgressFlag
Definition: InterfaceUnit.h:1201
TOnePrefDir::EraseFromPrefDirVectorAnd4MultiMap
void EraseFromPrefDirVectorAnd4MultiMap(int Caller, int HLoc, int VLoc)
Erase element at HLoc and VLoc from the PrefDirVector and from the 4MultiMap. Note that this entails ...
Definition: TrackUnit.cpp:13895
Simple
@ Simple
Definition: TrackUnit.h:66
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:809
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
Definition: TrainUnit.h:361
TUtilities::RHSignalFlag
bool RHSignalFlag
new at v2.3.0 false=LH signals
Definition: Utilities.h:79
TInterface::DeleteMenuItem
TMenuItem * DeleteMenuItem
Definition: InterfaceUnit.h:472
TTrack::NumberOfGaps
int NumberOfGaps(int Caller)
Returns the number of gaps in the railway.
Definition: TrackUnit.cpp:2935
TInterface::TTClockAdjustOKButtonClick
void __fastcall TTClockAdjustOKButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17433
TInterface::ChangeDirectionMenuItemClick
void __fastcall ChangeDirectionMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:13939
TInterface::PointsFlashDuration
float PointsFlashDuration
duration of the flash period when changing points manually
Definition: InterfaceUnit.h:1423
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:1064
TInterface::LocationNameComboBoxClick
void __fastcall LocationNameComboBoxClick(TObject *Sender)
Definition: InterfaceUnit.cpp:6323
TInterface::TTServiceSyntaxCheckKeyFlag
bool TTServiceSyntaxCheckKeyFlag
Definition: InterfaceUnit.h:1411
TInterface::UserToNum
bool UserToNum(AnsiString UserName, unsigned char &RlyUserNumber)
Definition: InterfaceUnit.cpp:28949
TInterface::TLevel2OperMode
TLevel2OperMode
Definition: InterfaceUnit.h:1054
TInterface::ConsecSignalsRoute
bool ConsecSignalsRoute
true for signal to next signal, true or false for autosigs & prefdir routes, and always false for unr...
Definition: InterfaceUnit.h:1284
TOnePrefDir::TPrefDirVectorIterator
std::vector< TPrefDirElement >::iterator TPrefDirVectorIterator
Definition: TrackUnit.h:1411
TOnePrefDir::CheckPrefDirAgainstTrackVectorNoMessage
bool CheckPrefDirAgainstTrackVectorNoMessage(int Caller)
Check loaded PrefDir against loaded track, and if discrepancies found clear EveryPrefDir & PrefDir4Mu...
Definition: TrackUnit.cpp:14146
TTrack::WriteTrackAndTextToImage
void WriteTrackAndTextToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveImageNoGrid1Click, TInterface::SaveImageAndGrid1Click and TInterface::SaveI...
Definition: TrackUnit.cpp:3951
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5992
TTrain::BackgroundColour
TColor BackgroundColour
< Used for writing to operating image when long refs showing
Definition: TrainUnit.h:544
TInterface::LengthCancelButtonClick
void __fastcall LengthCancelButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1736
TInterface::MPCPSendButton
TButton * MPCPSendButton
Definition: InterfaceUnit.h:708
TTrain
Definition: TrainUnit.h:314
TInterface::OutputLog5
TLabel * OutputLog5
Definition: InterfaceUnit.h:344
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:12464
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:299
TTextHandler::SelectTextVector
TTextVector SelectTextVector
Definition: TextUnit.h:71
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:12546
TInterface::CallOnImage
TImage * CallOnImage
Definition: InterfaceUnit.h:303
TInterface::LocationNameTextBox
TEdit * LocationNameTextBox
edit box that accepts location names
Definition: InterfaceUnit.h:134
TInterface::MPCPHostPortEditBoxKeyUp
void __fastcall MPCPHostPortEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:28410
TTrack::TActiveLevelCrossing::VLoc
int VLoc
VLoc value for found level crossing element.
Definition: TrackUnit.h:633
TInterface::SaveSessionFlag
bool SaveSessionFlag
true when a session save command has been given - implemented at next clock tick
Definition: InterfaceUnit.h:1340
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:924
TTrack::GetTruePositionsFromScreenPos
void GetTruePositionsFromScreenPos(int Caller, int &HPos, int &VPos, int ScreenPosH, int ScreenPosV)
Converse of GetScreenPositionsFromTruePos.
Definition: TrackUnit.cpp:7913
TInterface::EndSimulationMenuItem
TMenuItem * EndSimulationMenuItem
Definition: InterfaceUnit.h:701
TInterface::IsPerfLogFormObscuringFloatingLabel
bool IsPerfLogFormObscuringFloatingLabel(int Caller)
function to determine if floating label behind performance log, returns true if so
Definition: InterfaceUnit.cpp:22010
TInterface::SpeedButton73
TSpeedButton * SpeedButton73
Definition: InterfaceUnit.h:609
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TUtilities::DefaultTrackLength
int DefaultTrackLength
length of each track element before being changed within the program (can be changed in config....
Definition: Utilities.h:95
TInterface::TTLabel4
TLabel * TTLabel4
Definition: InterfaceUnit.h:385
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:513
TInterface::ConflictAnalysisKeyFlag
bool ConflictAnalysisKeyFlag
Definition: InterfaceUnit.h:1417
GapJump
@ GapJump
Definition: TrackUnit.h:66
TInterface::Level2OperMode
enum TInterface::TLevel2OperMode Level2OperMode
TInterface::SigsOnRightImage1
TImage * SigsOnRightImage1
Definition: InterfaceUnit.h:330
TInterface::UserToIPAndPort
bool UserToIPAndPort(AnsiString UserName, AnsiString &UserIP, short UserPort)
Definition: InterfaceUnit.cpp:28989
TInterface::SaveRailwayPDPButton
TBitBtn * SaveRailwayPDPButton
Save button on PrefDirPanel.
Definition: InterfaceUnit.h:159
TInterface::SpeedButton62
TSpeedButton * SpeedButton62
Definition: InterfaceUnit.h:598
TInterface::HostCombinedDynamicMap
TDynamicMap HostCombinedDynamicMap
Definition: InterfaceUnit.h:1152
TInterface::SpeedButton58
TSpeedButton * SpeedButton58
Definition: InterfaceUnit.h:594
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:893
TInterface::TempTTFileName
AnsiString TempTTFileName
the name for the temporary file used to save loaded timetables for storage in session files & error l...
Definition: InterfaceUnit.h:1266
TInterface::TTEVIterator
std::vector< AnsiString >::iterator TTEVIterator
typedef for pointers to entries in edit timetable functions
Definition: InterfaceUnit.h:1224
TInterface::PrefDirMode
@ PrefDirMode
Definition: InterfaceUnit.h:1016
TInterface::PassRedSignalMenuItem
TMenuItem * PassRedSignalMenuItem
Definition: InterfaceUnit.h:496
TTrack::AnyLinkedLevelCrossingElementsWithRoutesOrTrains
bool AnyLinkedLevelCrossingElementsWithRoutesOrTrains(int Caller, int HLoc, int VLoc, TPrefDirVector SearchVector, bool &TrainPresent)
True if a route or train present on any linked level crossing element.
Definition: TrackUnit.cpp:7659
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TRailGraphics::bmGreenRect
Graphics::TBitmap * bmGreenRect
Definition: GraphicUnit.h:525
TInterface::TTClockExitButtonClick
void __fastcall TTClockExitButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16774
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrack::IsReadyForOperation
bool IsReadyForOperation(bool GiveMessage)
Indicates whether or not the railway is ready for saving as a '.rly' file and for operation.
Definition: TrackUnit.h:845
TTrack::Raising
@ Raising
Definition: TrackUnit.h:614
TInterface::LastHostDataReceived
TDateTime LastHostDataReceived
player records this to allow updating when connection lost
Definition: InterfaceUnit.h:1155
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:10653
TTrack::UserGraphicMove
void UserGraphicMove(int Caller, int HPosInput, int VPosInput, int &UserGraphicItem, int &UserGraphicMoveHPos, int &UserGraphicMoveVPos, bool &UserGraphicFoundFlag)
handles moving of user graphics
Definition: TrackUnit.cpp:11140
TTextHandler::FindText
bool FindText(int Caller, AnsiString Name, int &HPos, int &VPos)
look in TextVector for text item 'Name', and if found return true and return its position in &HPos an...
Definition: TextUnit.cpp:588
TDisplay::DisplayOffsetV
static int DisplayOffsetV
the vertical offset of the displayed screen (as viewpoint moves down [railway moves up] this offset i...
Definition: DisplayUnit.h:79
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:381
TInterface::SpeedButton5
TSpeedButton * SpeedButton5
Definition: InterfaceUnit.h:541
TInterface::LengthHeatMapImageRedLow
TImage * LengthHeatMapImageRedLow
Definition: InterfaceUnit.h:313
TInterface::LocationNameKeyUp
void __fastcall LocationNameKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:1295
TInterface::LoadUserGraphicDialog
TOpenDialog * LoadUserGraphicDialog
Definition: InterfaceUnit.h:523
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1714
TInterface::OutputLog8MouseDown
void __fastcall OutputLog8MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16240
TInterface::SetPausedOrZoomedInfoCaption
void SetPausedOrZoomedInfoCaption(int Caller)
Definition: InterfaceUnit.cpp:25255
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:381
TInterface::CurDir
AnsiString CurDir
the full path to the folder where railway.exe resides
Definition: InterfaceUnit.h:1252
TInterface::OneEntryTimetableMemoKeyUp
void __fastcall OneEntryTimetableMemoKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:6351
TInterface::RouteCancelButton
TBitBtn * RouteCancelButton
Definition: InterfaceUnit.h:238
TInterface::SpeedButton26
TSpeedButton * SpeedButton26
Definition: InterfaceUnit.h:562
TPrefDirElement::SetELink
void SetELink(int input)
Used in pasting pref dirs.
Definition: TrackUnit.h:340
TInterface::HomeButton
TBitBtn * HomeButton
Definition: InterfaceUnit.h:293
TRailGraphics
Handles graphic data & functions, single object defined.
Definition: GraphicUnit.h:309
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:375
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:235
TInterface::FlashControlButtonClick
void __fastcall FlashControlButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27094
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:753
TInterface::NewHomeButton
TBitBtn * NewHomeButton
Definition: InterfaceUnit.h:294
TInterface::ResetAll
void ResetAll(int Caller)
Called during ClearEverything and on startup to reset all major railway data values.
Definition: InterfaceUnit.cpp:22094
TInterface::ConvertToOtherHandSignalsMenuItem
TMenuItem * ConvertToOtherHandSignalsMenuItem
Definition: InterfaceUnit.h:501
TInterface::SkipAllEventsBeforeNewService
void SkipAllEventsBeforeNewService(int Caller, int TrainID, int PtrAdvance)
used when change early to the next service (Fns, Fns-sh, Frh-sh or F-nshs) to advance the action poin...
Definition: InterfaceUnit.cpp:15077
TInterface::MPCPLabel5
TLabel * MPCPLabel5
Definition: InterfaceUnit.h:722
TInterface::SpeedButton75
TSpeedButton * SpeedButton75
Definition: InterfaceUnit.h:611
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:17867
TInterface::DistancesMarked
bool DistancesMarked
true when setting lengths, used to ensure the screen distance markers are redisplayed when the screen...
Definition: InterfaceUnit.h:1294
TPrefDirElement::GetXLink
int GetXLink() const
Returns XLink.
Definition: TrackUnit.h:281
TInterface::PlayerInSessionFlag
bool PlayerInSessionFlag
Definition: InterfaceUnit.h:1206
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:886
TTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:337
TTrain::TrainInFront
bool TrainInFront
Definition: TrainUnit.h:514
TInterface::TrackBuildPanel
TPanel * TrackBuildPanel
'Build/modify railway' panel
Definition: InterfaceUnit.h:400
TInterface::SkipListExitImageClick
void __fastcall SkipListExitImageClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14851
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:485
TInterface::InvertTTEntryKeyFlag
bool InvertTTEntryKeyFlag
Definition: InterfaceUnit.h:1404
TInterface::CutMoving
@ CutMoving
Definition: InterfaceUnit.h:1066
ActionsDueForm
TActionsDueForm * ActionsDueForm
Definition: ActionsDueUnit.cpp:15
TInterface::SpeedButton134
TSpeedButton * SpeedButton134
Definition: InterfaceUnit.h:670
TInterface::SelectLengthsMenuItem
TMenuItem * SelectLengthsMenuItem
Definition: InterfaceUnit.h:473
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1420
TInterface::SpeedButton72
TSpeedButton * SpeedButton72
Definition: InterfaceUnit.h:608
TInterface::ValidateTimetableButton
TButton * ValidateTimetableButton
Definition: InterfaceUnit.h:184
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6280
TInterface::UpdateDynamicMapFromTimeToExitMultiMap
void UpdateDynamicMapFromTimeToExitMultiMap(int Caller, TDynamicMap &DMap)
Convert TimeToExitMultiMap to an existing DynamicMap with all THVShortPairs listed.
Definition: InterfaceUnit.cpp:28818
TUserGraphicItem
Definition: DisplayUnit.h:32
TInterface::IPCheckLinkLabelLinkClick
void __fastcall IPCheckLinkLabelLinkClick(TObject *Sender, const UnicodeString Link, TSysLinkType LinkType)
Definition: InterfaceUnit.cpp:27485
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:72
TOneRoute::RouteFlash
TRouteFlash RouteFlash
the class member that allows the route to flash during setting up (see TRouteFlash above)
Definition: TrackUnit.h:1568
InterfaceUnit.h
TInterface::CopyMenuItemClick
void __fastcall CopyMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11840
TInterface::PerformanceLogButton
TBitBtn * PerformanceLogButton
Definition: InterfaceUnit.h:240
TInterface::FORMATTEDTT_DIR_NAME
static const UnicodeString FORMATTEDTT_DIR_NAME
Definition: InterfaceUnit.h:1050
TInterface::HighLightOneGap
bool HighLightOneGap(int Caller, int &HLoc, int &VLoc)
Called during gap setting to mark a gap with a red ellipse and ask user to select the corresponding g...
Definition: InterfaceUnit.cpp:18184
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:415
TUtilities::ModerateSignalChangeEventsPerFailure
int ModerateSignalChangeEventsPerFailure
Definition: Utilities.h:64
TUtilities::MinorMTBTSRs
int MinorMTBTSRs
Definition: Utilities.h:62
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7492
TTrack::TActiveLevelCrossing::BarrierState
TBarrierState BarrierState
state of barriers - Raising, Lowering, Up, Down (an enum - see above)
Definition: TrackUnit.h:625
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:355
StaticFeaturesDisplay
TDisplay * StaticFeaturesDisplay
The object pointer for track, text & graphics only for replacing the text of long service references.
Definition: DisplayUnit.cpp:56
TInterface::SpeedButton55
TSpeedButton * SpeedButton55
Definition: InterfaceUnit.h:591
TInterface::NextTTEntryButton
TButton * NextTTEntryButton
Definition: InterfaceUnit.h:169
TTextHandler::SelectTextVectorSize
unsigned int SelectTextVectorSize(int Caller)
return the number of items in SelectTextVector
Definition: TextUnit.cpp:547
TInterface::TrackTrainFloat
void TrackTrainFloat(int Caller)
Controls the floating window function, called during the ClockTimer2 function.
Definition: InterfaceUnit.cpp:20240
TDisplay::GetImage
TImage * GetImage()
Return a pointer to the screen image.
Definition: DisplayUnit.h:139
clB0G0R5
#define clB0G0R5
Definition: GraphicUnit.h:42
TInterface::AddLocationNameText
void AddLocationNameText(int Caller, AnsiString Name, int HPos, int VPos, bool UseEnteredPosition)
Add 'Name' to TextVector and display on screen at a position determined by the shape and size of the ...
Definition: InterfaceUnit.cpp:26241
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:11249
TInterface::TAnsiCouplingMapComp::operator()
bool operator()(const TAnsiCouplingPair &lower, const TAnsiCouplingPair &higher) const
Definition: InterfaceUnit.cpp:27496
TInterface::TRlyUserInfo::RlyUserNumber
unsigned char RlyUserNumber
Definition: InterfaceUnit.h:1104
TRailGraphics::CouplingExit4
Graphics::TBitmap * CouplingExit4
Definition: GraphicUnit.h:559
TInterface::TDMIt
TDynamicMap::iterator TDMIt
Definition: InterfaceUnit.h:1143
TTextHandler::CheckTextElementsInFile
bool CheckTextElementsInFile(int Caller, std::ifstream &VecFile)
check the validity of text items in VecFile prior to loading, return true for success
Definition: TextUnit.cpp:369
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:305
TTrack::GetVLocMax
int GetVLocMax()
Definition: TrackUnit.h:900
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:17950
TInterface::MPCPCancelButtonClick
void __fastcall MPCPCancelButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:28291
TInterface::TrainLongServRefInfoOnOffMenuItem
TMenuItem * TrainLongServRefInfoOnOffMenuItem
Definition: InterfaceUnit.h:772
TInterface::MPCPSendButtonClick
void __fastcall MPCPSendButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:28260
TInterface::HighlightOneEntryInAllEntriesTTListBox
void HighlightOneEntryInAllEntriesTTListBox(int Caller, int Position)
Called during timetable editing to highlight a single entry in the list of all entries in the left ha...
Definition: InterfaceUnit.cpp:7275
TInterface::DistanceStart
@ DistanceStart
Definition: InterfaceUnit.h:1065
TInterface::ErrorMessage
TMemo * ErrorMessage
the text of the normal error message screen
Definition: InterfaceUnit.h:433
TInterface::ExitPrefDirButton
TBitBtn * ExitPrefDirButton
Definition: InterfaceUnit.h:161
TUtilities::ModeratePointChangeEventsPerFailure
int ModeratePointChangeEventsPerFailure
Definition: Utilities.h:63
TTrack::LNPendingList
TLNPendingList LNPendingList
list of location name elements awaiting processing (see type for more information above)
Definition: TrackUnit.h:817
TInterface::TIVIt
TInfoVector::iterator TIVIt
Definition: InterfaceUnit.h:1113
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:125
TRailGraphics::CouplingExit1
Graphics::TBitmap * CouplingExit1
Definition: GraphicUnit.h:556
TInterface::TrackLengthPanel
TPanel * TrackLengthPanel
the panel that contains the distance/speed setting buttons and edit boxes
Definition: InterfaceUnit.h:419
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:812
TRailGraphics::bmSolidBgnd
Graphics::TBitmap * bmSolidBgnd
Definition: GraphicUnit.h:1026
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1027
TTrack::GroundSignalBuild
@ GroundSignalBuild
Definition: TrackUnit.h:876
TInterface::MPCPHostPortEditBox
TEdit * MPCPHostPortEditBox
Definition: InterfaceUnit.h:725
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:898
TUtilities::ReadOneLineFromCouplingFile
bool ReadOneLineFromCouplingFile(std::ifstream &InFile, AnsiString &OutString)
reads a single line from the multiplayer coupling file, returns true for success with OutString conta...
Definition: Utilities.cpp:642
TInterface::MoveTextOrGraphicButton
TBitBtn * MoveTextOrGraphicButton
Definition: InterfaceUnit.h:105
TInterface::RouteContinuing
@ RouteContinuing
Definition: InterfaceUnit.h:1071
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:887
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:439
SignalPost
@ SignalPost
Definition: TrackUnit.h:66
TTrack::SetAllDefaultLengthsAndSpeedLimits
void SetAllDefaultLengthsAndSpeedLimits(int Caller)
Work through all elements in TrackVector setting all lengths & speed limits to default values - inclu...
Definition: TrackUnit.cpp:9827
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:490
TAllRoutes::WriteAllRoutesToImage
void WriteAllRoutesToImage(int Caller, Graphics::TBitmap *Bitmap)
Calls RouteImageMarker for each route in turn to display the route colours and direction arrows on th...
Definition: TrackUnit.cpp:19873
TInterface::MajorFailuresMenuItemClick
void __fastcall MajorFailuresMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27078
IDInt::GetInt
int GetInt() const
get the internal integer
Definition: TrackUnit.h:509
TInterface::InfoPanel
TPanel * InfoPanel
the general information panel (with blue 'i' symbol)
Definition: InterfaceUnit.h:415
TInterface::TServiceInfo::CheckOK
bool CheckOK()
Definition: InterfaceUnit.cpp:29050
TInterface::TextOrUserGraphicGridButtonClick
void __fastcall TextOrUserGraphicGridButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1957
TAllRoutes::TAllRoutesVectorIterator
std::vector< TOneRoute >::iterator TAllRoutesVectorIterator
Definition: TrackUnit.h:1681
TInterface::FormKeyDown
void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:15644
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1691
TInterface::MoveTTEntryDownButton
TButton * MoveTTEntryDownButton
Definition: InterfaceUnit.h:175
TInterface::CheckTimetableFromSessionFile
bool CheckTimetableFromSessionFile(int Caller, std::ifstream &SessionFile)
Check the timetable file embedded within a session file & return false for error, called during Sessi...
Definition: InterfaceUnit.cpp:24244
TRailGraphics::SpeedBut71GrndBlackGlyph
Graphics::TBitmap * SpeedBut71GrndBlackGlyph
Definition: GraphicUnit.h:1096
TInterface::DeleteTTEntryButtonClick
void __fastcall DeleteTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4593
TTrack::GetScreenPositionsFromTruePos
void GetScreenPositionsFromTruePos(int Caller, int &ScreenPosH, int &ScreenPosV, int HPosTrue, int VPosTrue)
With large railways only part of the railway is displayed on screen, and this function converts true ...
Definition: TrackUnit.cpp:7927
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:13724
TTrack::PlotPlainLoweredLinkedLevelCrossingBarriersAndSetMarkers
void PlotPlainLoweredLinkedLevelCrossingBarriersAndSetMarkers(int Caller, int BaseElementSpeedTag, int HLoc, int VLoc, TDisplay *Disp, bool Manual)
Plot LC elements without any base elements, and set LCPlotted true - used in ClearandRebuildRailway (...
Definition: TrackUnit.cpp:7105
TInterface::WarningFlashCount
int WarningFlashCount
increments each clock cycle to a max. of 4 then resets to 0, used to toggle bool WarningFlash - see a...
Definition: InterfaceUnit.h:1491
TInterface::StartWholeRailwayMoveHPos
int StartWholeRailwayMoveHPos
mouse X position when start to move the whole railway
Definition: InterfaceUnit.h:1469
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:217
TInterface::SpeedButton93
TSpeedButton * SpeedButton93
Definition: InterfaceUnit.h:629
TPrefDirElement::AutoSignals
bool AutoSignals
marker within the route for an AutoSignal route element
Definition: TrackUnit.h:230
TInterface::SpeedButton119
TSpeedButton * SpeedButton119
Definition: InterfaceUnit.h:655
ActionsDueUnit.h
TInterface::SelectedGraphicFileName
AnsiString SelectedGraphicFileName
filename for selected graphic set during LoadGraphic
Definition: InterfaceUnit.h:1270
TTrain::LongServRefEnteredFlag
bool LongServRefEnteredFlag
defines whether service ref plotted or not
Definition: TrainUnit.h:497
TTrack::EraseLocationAndActiveTrackElementNames
void EraseLocationAndActiveTrackElementNames(int Caller, AnsiString LocationName)
Examines LocationNameMultiMap and if the LocationName is found all elements at that H & V (in both ac...
Definition: TrackUnit.cpp:9125
TInterface::TrackBuildPanelLabel
TLabel * TrackBuildPanelLabel
label to the left of TrackBuildPanel
Definition: InterfaceUnit.h:361
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:298
TInterface::TTClockAdjButtonClick
void __fastcall TTClockAdjButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16743
TInterface::RAILWAY_DIR_NAME
static const UnicodeString RAILWAY_DIR_NAME
Definition: InterfaceUnit.h:1045
TAllRoutes::MarkAllRoutes
void MarkAllRoutes(int Caller, TDisplay *Disp)
Calls PrefDirMarker to display all routes, with RouteCall set to identify a route call,...
Definition: TrackUnit.cpp:19858
TInterface::SPADImage
TImage * SPADImage
Definition: InterfaceUnit.h:304
TInterface::AddTrackButton
TBitBtn * AddTrackButton
Definition: InterfaceUnit.h:101
TInterface::MainMenu1
TMainMenu * MainMenu1
the program menu
Definition: InterfaceUnit.h:430
TInterface::OutputLog7MouseDown
void __fastcall OutputLog7MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16230
TInterface::LoadClipboard
void LoadClipboard(int Caller)
Load system clipboard to allow cutting & pasting between separate railway applications.
Definition: InterfaceUnit.cpp:26495
Concourse
@ Concourse
Definition: TrackUnit.h:67
TTextHandler::TTextVectorIterator
std::vector< TTextItem >::iterator TTextVectorIterator
Definition: TextUnit.h:68
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:901
TInterface::LocationNamesNotSetImage
TImage * LocationNamesNotSetImage
Definition: InterfaceUnit.h:327
TInterface::FloatingPanel
TPanel * FloatingPanel
new for v2.2.0 where label sits in it and it autosizes to the label. Labels are not TWinControls so t...
Definition: InterfaceUnit.h:423
TRailGraphics::SpeedBut68NormBlackGlyph
Graphics::TBitmap * SpeedBut68NormBlackGlyph
Definition: GraphicUnit.h:1085
TInterface::CompileAllEntriesMemoAndSetIterators
void CompileAllEntriesMemoAndSetIterators(int Caller)
Used during timetable editing funtions to compile the list of entries into the left hand long entry w...
Definition: InterfaceUnit.cpp:6483
TTrack::SelectVector
TTrackVector SelectVector
vectors of TrackElements
Definition: TrackUnit.h:829
TRailGraphics::SolidCircleYellow
Graphics::TBitmap * SolidCircleYellow
Definition: GraphicUnit.h:566
TInterface::SaveMenuItemClick
void __fastcall SaveMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2825
TInterface::ScreenGridButtonClick
void __fastcall ScreenGridButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2067
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:437
TInterface::UserGraphicMoveHPos
int UserGraphicMoveHPos
Definition: InterfaceUnit.h:1489
TInterface::PassRedSignalMenuItemClick
void __fastcall PassRedSignalMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:15231
TInterface::ReselectMenuItem
TMenuItem * ReselectMenuItem
Definition: InterfaceUnit.h:465
TOnePrefDir::WritePrefDirToImage
void WritePrefDirToImage(int Caller, Graphics::TBitmap *Bitmap)
Used when creating a bitmap image to display preferred directions (as on screen during 'Set preferred...
Definition: TrackUnit.cpp:14863
TInterface::MajorDelaysMenuItemClick
void __fastcall MajorDelaysMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27017
TInterface::ClearEverything
bool ClearEverything(int Caller)
First check whether a railway file has changed and if so ask user if really wants to close it without...
Definition: InterfaceUnit.cpp:18220
TInterface::PrefDirContinuing
@ PrefDirContinuing
Definition: InterfaceUnit.h:1060
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:509
TRailGraphics::SpeedBut70NormBlackGlyph
Graphics::TBitmap * SpeedBut70NormBlackGlyph
Definition: GraphicUnit.h:1087
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:896
TInterface::SaveAsMenuItem
TMenuItem * SaveAsMenuItem
Definition: InterfaceUnit.h:445
TInterface::MoveTextOrGraphicButtonClick
void __fastcall MoveTextOrGraphicButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1210
TInterface::NumHVPairCheckOK
bool NumHVPairCheckOK(TNumHVPair NumHVPair)
check for datagram validity
Definition: InterfaceUnit.cpp:29031
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:860
TInterface::SpeedHeatMapImageRedHigh
TImage * SpeedHeatMapImageRedHigh
Heatmap images.
Definition: InterfaceUnit.h:316
TTrack::NoGaps
bool NoGaps(int Caller)
True if there are no gaps.
Definition: TrackUnit.cpp:4660
TInterface::ZoomButton
TBitBtn * ZoomButton
Definition: InterfaceUnit.h:295
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1928
TInterface::PrefDirConflictAdviceMessageSent
bool PrefDirConflictAdviceMessageSent
indicates that the advisory message drawing attention to the pref dir conflict checker has been given
Definition: InterfaceUnit.h:1326
TextHandler
TTextHandler * TextHandler
Definition: TextUnit.cpp:98
TInterface::CopyTTEntryButton
TButton * CopyTTEntryButton
Definition: InterfaceUnit.h:170
TInterface::ErrorMessageStoreImage
TMemo * ErrorMessageStoreImage
the text of the error message for failure to draw trains in SaveOperatingImage
Definition: InterfaceUnit.h:435
TInterface::SpeedButton76
TSpeedButton * SpeedButton76
Definition: InterfaceUnit.h:612
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:513
TInterface::SpeedButton145
TSpeedButton * SpeedButton145
Definition: InterfaceUnit.h:681
TInterface::NoPrefDirMode
@ NoPrefDirMode
Definition: InterfaceUnit.h:1060
TInterface::SpeedButton88
TSpeedButton * SpeedButton88
Definition: InterfaceUnit.h:624
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:894
TInterface::MoveTTEntryDownButtonClick
void __fastcall MoveTTEntryDownButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5185
TRailGraphics::SpeedBut72GrndBlackGlyph
Graphics::TBitmap * SpeedBut72GrndBlackGlyph
Definition: GraphicUnit.h:1097
TInterface::SpeedButton28
TSpeedButton * SpeedButton28
Definition: InterfaceUnit.h:564
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:305
TAllRoutes::LevelCrossingBarrierUpDelay
const float LevelCrossingBarrierUpDelay
< used to flag the fact that a route is being truncated in order to change the behaviour of signal as...
Definition: TrackUnit.h:1734
TInterface::SpeedTopLabel
TLabel * SpeedTopLabel
Definition: InterfaceUnit.h:220
TInterface::OperateButton
TBitBtn * OperateButton
Definition: InterfaceUnit.h:234
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:296
TInterface::SignallerJoinedByMenuItemClick
void __fastcall SignallerJoinedByMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14051
TTrack::MirrorArray
int MirrorArray[FirstUnusedSpeedTagNumber]
holds TrackElement SpeedTag values for 'mirroring' via menu items 'Edit' & 'Mirror'
Definition: TrackUnit.h:789
TUserGraphicItem::Width
int Width
Definition: DisplayUnit.h:36
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TInterface::IsActionsDueFormObscuringFloatingLabel
bool IsActionsDueFormObscuringFloatingLabel(int Caller)
function to determine if floating label behind actions due form, returns true if so
Definition: InterfaceUnit.cpp:22035
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:139
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:513
TInterface::SkipFormResizeEvent
bool SkipFormResizeEvent
added at v2.1.0 to avoid calling the event during startup and shutdown
Definition: InterfaceUnit.h:1354
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:18771
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:479
TInterface::TrackInfoOnOffMenuItem
TMenuItem * TrackInfoOnOffMenuItem
Definition: InterfaceUnit.h:479
TInterface::NumToIPAndPort
bool NumToIPAndPort(unsigned char RlyUserNumber, AnsiString &UserIP, short UserPort)
Definition: InterfaceUnit.cpp:29010
TDisplay::ShowWarningLog
void ShowWarningLog(int Caller)
Show the warnings after timetable clock adjusted.
Definition: DisplayUnit.cpp:567
TInterface::SpeedButton121
TSpeedButton * SpeedButton121
Definition: InterfaceUnit.h:657
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:11676
TInterface::RLIt
TRailwayList::iterator RLIt
Definition: InterfaceUnit.h:1098
TInterface::SaveAsMenuItemClick
void __fastcall SaveAsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2878
TTextItem
A single piece of text that can be displayed on the railway.
Definition: TextUnit.h:37
TInterface::SpeedButton136
TSpeedButton * SpeedButton136
Definition: InterfaceUnit.h:672
TUtilities::NoPlatsMessageSent
bool NoPlatsMessageSent
Definition: Utilities.h:85
TInterface::PointFlash
TGraphicElement * PointFlash
Definition: InterfaceUnit.h:1503
TInterface::MPCPLabel8
TLabel * MPCPLabel8
Definition: InterfaceUnit.h:729
TInterface::TrackLinkedImage
TImage * TrackLinkedImage
Definition: InterfaceUnit.h:322
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:381
TInterface::MPCPReadyToBeginButton
TButton * MPCPReadyToBeginButton
Definition: InterfaceUnit.h:711
TInterface::SelectPickedUp
bool SelectPickedUp
true when a valid selected screen area has been clicked after a 'Copy' or 'Cut' selected in the 'Edit...
Definition: InterfaceUnit.h:1348
TDisplay::ResetZoomOutOffsets
void ResetZoomOutOffsets()
Reset the zoomed-out screen display to the 'Home' position.
Definition: DisplayUnit.h:209
TTrackElement::ThreeAspect
@ ThreeAspect
Definition: TrackUnit.h:161
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:85
TInterface::DevelopmentPanel
TPanel * DevelopmentPanel
used for diagnostic purposes, made visible by ctrl+ alt+ 3
Definition: InterfaceUnit.h:421
TTrack::NoActiveOrInactiveTrack
bool NoActiveOrInactiveTrack(int Caller)
True if there is no active or inactive track in the railway.
Definition: TrackUnit.cpp:1947
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:389
TTrack::MultiplayerOverlayMap
TMultiplayerOverlayMap MultiplayerOverlayMap
Definition: TrackUnit.h:799
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:95
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:846
TInterface::CopyMoving
@ CopyMoving
Definition: InterfaceUnit.h:1066
TInterface::TimetableEditPanel
TPanel * TimetableEditPanel
the large panel that contains all the main timetable components
Definition: InterfaceUnit.h:409
TInterface::RouteMode
enum TInterface::@0 RouteMode
route building modes
TDisplay::DisplayZoomOutOffsetVHome
static int DisplayZoomOutOffsetVHome
the vertical offset of the zoomed-out 'Home' display
Definition: DisplayUnit.h:91
TInterface::LengthsHeatmapButtonClick
void __fastcall LengthsHeatmapButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:29425
TInterface::TrainStatusInfoOnOffMenuItem
TMenuItem * TrainStatusInfoOnOffMenuItem
Definition: InterfaceUnit.h:480
TInterface::TimeWarningSent
bool TimeWarningSent
true when warning about time timetable order has been given
Definition: InterfaceUnit.h:1278
TInterface::SetLengthsButtonClick
void __fastcall SetLengthsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1508
TInterface::SaveTTEntryButton
TButton * SaveTTEntryButton
Definition: InterfaceUnit.h:176
TAllRoutes::Route2MultiMap
TRoute2MultiMap Route2MultiMap
the map that stores the elements of all routes on the railway (see TRoute2MultiMap for more info)
Definition: TrackUnit.h:1750
TInterface::SpeedButton37
TSpeedButton * SpeedButton37
Definition: InterfaceUnit.h:573
TInterface::BaseMode
@ BaseMode
Definition: InterfaceUnit.h:1016
TInterface::SpeedEditBoxKeyUp
void __fastcall SpeedEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:16434
TTrack::TTrackMap
std::map< THVPair, unsigned int, TMapComp > TTrackMap
map of TrackElement TrackVectorPositions, HLoc & VLoc pair is the key
Definition: TrackUnit.h:660
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:12258
TInterface::MirrorMenuItem
TMenuItem * MirrorMenuItem
Definition: InterfaceUnit.h:469
TInterface::EveryPrefDir
TOnePrefDir * EveryPrefDir
all the Pref Dir elements in the railway
Definition: InterfaceUnit.h:1516
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:878
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TInterface::TServiceInfo
Definition: InterfaceUnit.h:1123
TInterface::OutputLog8
TLabel * OutputLog8
Definition: InterfaceUnit.h:347
TInterface::SpeedButton1
TSpeedButton * SpeedButton1
See Speedbutton1 detail for track element allocations.
Definition: InterfaceUnit.h:537
TInterface::MinorDelaysMenuItem
TMenuItem * MinorDelaysMenuItem
Definition: InterfaceUnit.h:747
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:932
TInterface::SaveAsSubroutine
void SaveAsSubroutine(int Caller)
Used to save a railway when not already saved - e.g. when not already named or when the 'Save as' men...
Definition: InterfaceUnit.cpp:25969
TInterface::DynMapFromHost
TDynamicMap DynMapFromHost
Definition: InterfaceUnit.h:1152
TInterface::MainScreenMouseDown2
void MainScreenMouseDown2(int Caller, TMouseButton Button, TShiftState Shift, int X, int Y)
Called when mouse button clicked in zoom-in mode.
Definition: InterfaceUnit.cpp:7521
TOneRoute::ClearRoute
void ClearRoute()
Empty the route of any stored elements.
Definition: TrackUnit.h:1574
TInterface::RepairFailedTrainMenuItemClick
void __fastcall RepairFailedTrainMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14170
TInterface::AddLocationName
@ AddLocationName
Definition: InterfaceUnit.h:1065
TTrack::LocationNameAllocated
bool LocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found in LocationNameMultiMap.
Definition: TrackUnit.cpp:8922
TInterface::MPHPOwnIPEditBox
TEdit * MPHPOwnIPEditBox
Definition: InterfaceUnit.h:733
TTrack::IsTrackFinished
bool IsTrackFinished()
Indicates whether or not the track has been successfully linked together.
Definition: TrackUnit.h:851
TAllRoutes::AllRoutesClear
void AllRoutesClear()
Erases all routes from AllRoutesVector and from Route2MultiMap.
Definition: TrackUnit.h:1762
TRailGraphics::SpeedBut74GrndBlackGlyph
Graphics::TBitmap * SpeedBut74GrndBlackGlyph
Definition: GraphicUnit.h:1099
TInterface::ShowTTActionsListBox
void ShowTTActionsListBox(int Caller)
makes TTActionsListBox visible
Definition: InterfaceUnit.cpp:14787
TAllRoutes::PointsDelay
const float PointsDelay
the value in seconds for which points flash prior to being changed. Used for the points flash period ...
Definition: TrackUnit.h:1738
TInterface::SaveSession
void SaveSession(int Caller)
Save a session file - see LoadSession for details of additions to the session file.
Definition: InterfaceUnit.cpp:22302
TTrack::GetTrackElementFromAnyTrackMap
TTrackElement & GetTrackElementFromAnyTrackMap(int Caller, int HLoc, int VLoc, TTrackMap &Map, TTrackVector &Vector)
Return a reference to the element at HLoc & VLoc for any map and any vector (used for SelectPrefDir i...
Definition: TrackUnit.cpp:5903
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TAllRoutes::SignalsDelay
const float SignalsDelay
the value in seconds for which signals flash prior to being changed. Used for the route flash period ...
Definition: TrackUnit.h:1740
TDisplay::GetFont
TFont * GetFont()
Return the current screen font.
Definition: DisplayUnit.h:133
TInterface::SpeedButton10
TSpeedButton * SpeedButton10
Definition: InterfaceUnit.h:546
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
Definition: TrainUnit.h:329
TOneRoute::ReclaimSignalsForNonAutoSigRoutes
void ReclaimSignalsForNonAutoSigRoutes(int caller, TPrefDirElement LastPDElement, TPrefDirElement FirstPDElement)
Adds signal to front/end of green or red routes when blue route truncated or removed.
Definition: TrackUnit.cpp:19378
TInterface::SpeedButton103
TSpeedButton * SpeedButton103
Definition: InterfaceUnit.h:639
TInterface::SaveMultiplayerSessionMenuItem
TMenuItem * SaveMultiplayerSessionMenuItem
Definition: InterfaceUnit.h:703
TOnePrefDir::PrefDir4MultiMap
TPrefDir4MultiMap PrefDir4MultiMap
the pref dir multimap - up to 4 values (up to 2 tracks per element each with 2 directions)
Definition: TrackUnit.h:1346
TTrack::ThreeAspectBuild
@ ThreeAspectBuild
Definition: TrackUnit.h:876
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:873
TOnePrefDir::GetVectorPositionsFromPrefDir4MultiMap
void GetVectorPositionsFromPrefDir4MultiMap(int Caller, int HLoc, int VLoc, bool &FoundFlag, int &PrefDirPos0, int &PrefDirPos1, int &PrefDirPos2, int &PrefDirPos3)
Return up to 4 vector positions for a given HLoc & VLoc; unused values return -1.
Definition: TrackUnit.cpp:14234
TInterface::TempCursorSet
bool TempCursorSet
indicates that a screen cursor has been stored in TempCursor for redisplay after a temporary cursor (...
Definition: InterfaceUnit.h:1356
TOnePrefDir::GetPrefDirTruncateElement
bool GetPrefDirTruncateElement(int Caller, int HLoc, int VLoc)
Called during PrefDir build or distance setting. It truncates at & including the first element in the...
Definition: TrackUnit.cpp:13369
TInterface::SpeedButton85
TSpeedButton * SpeedButton85
Definition: InterfaceUnit.h:621
TTrack::LengthandSpeedMarker
void LengthandSpeedMarker(int Caller, TDisplay *Disp)
Examine all elements in the TrackVector and if have a valid length mark the relevant track using Mark...
Definition: TrackUnit.cpp:9862
TInterface::PasteMenuItemClick
void __fastcall PasteMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12811
TInterface::MPCPLabel6
TLabel * MPCPLabel6
Definition: InterfaceUnit.h:723
TInterface::MainScreenMouseUp
void __fastcall MainScreenMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:9719
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:87
TInterface::SaveTempTimetableFile
void SaveTempTimetableFile(int Caller, AnsiString InFileName)
Save a timetable as a temporary file either on loading directly or on loading a session file....
Definition: InterfaceUnit.cpp:25720
TAllRoutes::AllRoutesVector
TAllRoutesVector AllRoutesVector
the vector that stores all the routes on the railway
Definition: TrackUnit.h:1744
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
gives a delay od Msec value;
Definition: Utilities.cpp:108
TInterface::RotateMenuItemClick
void __fastcall RotateMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12100
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:231
TInterface::OverallDistance
int OverallDistance
Definition: InterfaceUnit.h:1446
TInterface::SelectStartPair
THVShortPair SelectStartPair
'Select' menu items
Definition: InterfaceUnit.h:1505
TInterface::SpeedButton105
TSpeedButton * SpeedButton105
Definition: InterfaceUnit.h:641
TInterface::SpeedButton83
TSpeedButton * SpeedButton83
Definition: InterfaceUnit.h:619
TTrack::TSRVector
TFailedElementVector TSRVector
vector of failed points with track vector positions & repair times for use in failure handling (new a...
Definition: TrackUnit.h:795
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:7341
TTrainController::MTBFHours
double MTBFHours
<Message flags in TT checks to stop being given twice
Definition: TrainUnit.h:865
TOnePrefDir::SaveSearchVector
void SaveSearchVector(int Caller, std::ofstream &VecFile)
Save the search vector to a file.
Definition: TrackUnit.cpp:13862
TInterface::RestoreAllDefaultLengthsButtonClick
void __fastcall RestoreAllDefaultLengthsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1865
TInterface::LengthWarningSentFlag
bool LengthWarningSentFlag
indicates that the length selection applying to all elements in the selection warning has been given,...
Definition: InterfaceUnit.h:1308
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:715
TInterface::SpeedButton131
TSpeedButton * SpeedButton131
Definition: InterfaceUnit.h:667
TInterface::FlipMenuItemClick
void __fastcall FlipMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11861
TInterface::api_main_mode_
int api_main_mode_
Definition: InterfaceUnit.h:1230
TInterface::TrainTTInfoOnOffMenuItem
TMenuItem * TrainTTInfoOnOffMenuItem
Definition: InterfaceUnit.h:773
TInterface::AllSetUpFlag
bool AllSetUpFlag
false during initial start up, true when all set up to allow MasterClock to start
Definition: InterfaceUnit.h:1272
TTrack::SaveChangingLCVector
void SaveChangingLCVector(int Caller, std::ofstream &OutFile)
Save all changing vector values (used for error file)
Definition: TrackUnit.cpp:3701
TInterface::LocationNameButton
TBitBtn * LocationNameButton
Definition: InterfaceUnit.h:106
TInterface::SpeedButton101
TSpeedButton * SpeedButton101
Definition: InterfaceUnit.h:637
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:494
TInterface::BuildDummyTestMap
void BuildDummyTestMap(TDynamicMap &DMap, std::ifstream &ExitFile)
Definition: InterfaceUnit.cpp:28783
TInterface::LoadRailway
void LoadRailway(int Caller, AnsiString LoadFileName)
Load a railway file. The Active elements marker now has a '1' at the end if there are user graphics t...
Definition: InterfaceUnit.cpp:2670
TInterface::TAnsiCouplingPair
std::pair< AnsiString, THVShortPair > TAnsiCouplingPair
Definition: InterfaceUnit.h:1078
TTrack::SaveUserGraphics
void SaveUserGraphics(int Caller, std::ofstream &VecFile)
save graphics
Definition: TrackUnit.cpp:12301
TInterface::RailwayList
TRailwayList RailwayList
Definition: InterfaceUnit.h:1099
TInterface::TextOrUserGraphicGridButton
TBitBtn * TextOrUserGraphicGridButton
Definition: InterfaceUnit.h:108
TInterface::PointFlashVectorPosition
int PointFlashVectorPosition
Definition: InterfaceUnit.h:1449
TInterface::AddSubMinsBoxKeyUp
void __fastcall AddSubMinsBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:6376
TInterface::ExitOperationButtonClick
void __fastcall ExitOperationButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2571
TGraphicElement::LoadOriginalScreenGraphic
void LoadOriginalScreenGraphic(int Caller)
Load original graphic from the screen for point flashing or route start markers.
Definition: TrackUnit.cpp:1830
TInterface::TTClockAdd1hButtonClick
void __fastcall TTClockAdd1hButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16991
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:831
TInterface::TTClockx2ButtonClick
void __fastcall TTClockx2ButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16819
TInterface::SpeedButton45
TSpeedButton * SpeedButton45
Definition: InterfaceUnit.h:581
TInterface::SpeedEditBox2KeyUp
void __fastcall SpeedEditBox2KeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:16590
TUtilities::ModerateMTBTSRs
int ModerateMTBTSRs
Definition: Utilities.h:65
TInterface::TimeOrderKeyFlag
bool TimeOrderKeyFlag
Definition: InterfaceUnit.h:1410
TInterface::SavedFileName
AnsiString SavedFileName
the full path and filename of the loaded railway
Definition: InterfaceUnit.h:1264
TInterface::FillSelectionMessageSentFlag
bool FillSelectionMessageSentFlag
indicates that the message about filling a selected area with a chosen track element has been given,...
Definition: InterfaceUnit.h:1300
TInterface::PlanPrefDirsMenuItemClick
void __fastcall PlanPrefDirsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2094
TInterface::OperatingPanel
TPanel * OperatingPanel
'Operate railway' panel
Definition: InterfaceUnit.h:406
TTrack::SelectVectorSize
unsigned int SelectVectorSize()
Return the number of selected active and inactive track elements (via menu items 'Edit' and 'Select')
Definition: TrackUnit.h:941
TInterface::SpeedButton141
TSpeedButton * SpeedButton141
Definition: InterfaceUnit.h:677
TInterface::TServiceInfo::TimeToExitSecs
short TimeToExitSecs
Definition: InterfaceUnit.h:1127
TTrack::GetGapVLoc
int GetGapVLoc()
Definition: TrackUnit.h:885
TInterface::SpeedButton124
TSpeedButton * SpeedButton124
Definition: InterfaceUnit.h:660
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:487
TUtilities::CheckFileString
bool CheckFileString(std::ifstream &InFile)
checks that the value is a string ('0' or CR accepted as delimiters), returns true for success
Definition: Utilities.cpp:423
TPrefDirElement::GetDirectionPrefDirGraphicPtr
Graphics::TBitmap * GetDirectionPrefDirGraphicPtr() const
picks up the EntryDirectionGraphicPtr for preferred directions
Definition: TrackUnit.cpp:1028
TOnePrefDir::LoadPrefDir
void LoadPrefDir(int Caller, std::ifstream &VecFile)
Load a vector and map of preferred directions from the file.
Definition: TrackUnit.cpp:13682
TRailGraphics::bmGrid
Graphics::TBitmap * bmGrid
Definition: GraphicUnit.h:526
TInterface::RestoreFocusPanel
TPanel * RestoreFocusPanel
Panel used to restore focus to Interface to enable cursor keys to move screen.
Definition: InterfaceUnit.h:397
TInterface::BlackBgndMenuItem
TMenuItem * BlackBgndMenuItem
Definition: InterfaceUnit.h:460
TInterface::ExitHeatmaps
void ExitHeatmaps()
utility function to set all heatmap values to normal (i.e. unused)
Definition: InterfaceUnit.cpp:29574
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:57
TInterface::DynMapToHost
TDynamicMap DynMapToHost
Definition: InterfaceUnit.h:1152
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:829
TTrack::TActiveLevelCrossing::BaseElementSpeedTag
int BaseElementSpeedTag
SpeedTag value for the base element of a level crossing.
Definition: TrackUnit.h:629
TInterface::FloatingLabel
TLabel * FloatingLabel
the floating window that displays track & train information
Definition: InterfaceUnit.h:378
TOnePrefDir::GetModifiablePrefDirElementAt
TPrefDirElement & GetModifiablePrefDirElementAt(int Caller, int At)
Return a modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:12558
TInterface::NewTTEntryButton
TButton * NewTTEntryButton
Definition: InterfaceUnit.h:178
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:707
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:871
TInterface::SubMinsButtonClick
void __fastcall SubMinsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3947
TGraphicElement
Allows a single Width x Height graphic to change and change back independently of the remaining displ...
Definition: TrackUnit.h:434
TInterface::TimetableControlMenuItemClick
void __fastcall TimetableControlMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:13769
TInterface::MPCPLabel4
TLabel * MPCPLabel4
Definition: InterfaceUnit.h:712
TTrack::TActiveTrackElementNameIterator
TActiveTrackElementNameMap::iterator TActiveTrackElementNameIterator
Definition: TrackUnit.h:709
TInterface::DistanceBox
TEdit * DistanceBox
distance/speed setting edit box that accepts distances
Definition: InterfaceUnit.h:130
TInterface::SpeedButton142
TSpeedButton * SpeedButton142
Definition: InterfaceUnit.h:678
TInterface::PresetAutoSigRoutesButton
TBitBtn * PresetAutoSigRoutesButton
Definition: InterfaceUnit.h:239
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:135
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TInterface::TTLabel12
TLabel * TTLabel12
Definition: InterfaceUnit.h:392
TInterface::TTServiceSyntaxCheckButtonClick
void __fastcall TTServiceSyntaxCheckButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5031
TInterface::SpeedButton57
TSpeedButton * SpeedButton57
Definition: InterfaceUnit.h:593
TInterface::TAnsiCouplingMapEntry
std::pair< TAnsiCouplingPair, TAnsiCouplingPair > TAnsiCouplingMapEntry
TAnsiCouplingMap... items are just used temporarily to load the CouplingFile into a RailwayName based...
Definition: InterfaceUnit.h:1079
TPerfLogForm::PerformanceLogBox
TMemo * PerformanceLogBox
Definition: PerfLogUnit.h:16
TInterface::SigAutoNonConsecButton
TBitBtn * SigAutoNonConsecButton
Definition: InterfaceUnit.h:755
TInterface::TTClockSpeed
float TTClockSpeed
rate at which the timetable clock runs 1 = normal
Definition: InterfaceUnit.h:1427
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:56
TDisplay::PlotAbsolute
void PlotAbsolute(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at the railway (not screen) position set by HPos & VPos.
Definition: DisplayUnit.cpp:491
TUtilities::FailureMode
TFailureMode FailureMode
specifies whether no failures or minor, moderate or major random failures are to be applied (added at...
Definition: Utilities.h:125
TTrack::TrackElementPresentAtHV
bool TrackElementPresentAtHV(int Caller, int HLoc, int VLoc)
New at v1.2.0; true if a track element present (not inactive elements - see InactiveTrackElementPrese...
Definition: TrackUnit.cpp:5951
TGraphicElement::LoadOriginalExistingGraphic
void LoadOriginalExistingGraphic(int Caller, int HOffset, int VOffset, int WidthIn, int HeightIn, Graphics::TBitmap *Graphic)
Load red or green gap flashing graphic from the stored bitmaps.
Definition: TrackUnit.cpp:1864
TInterface::TTClockx4ButtonClick
void __fastcall TTClockx4ButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16838
TOnePrefDir::CalcDistanceAndSpeed
void CalcDistanceAndSpeed(int Caller, int &OverallDistance, int &OverallSpeedLimit, bool &LeadingPointsAtLastElement)
Used when setting element lengths, returns in &OverallDistance the overall distance for the selected ...
Definition: TrackUnit.cpp:14801
TTextHandler::TextFound
bool TextFound(int Caller, int HPosInput, int VPosInput, AnsiString &Text)
< look for a text item in the vicinity of HPosInput & VPosInput & if TextToErase is null then erase a...
Definition: TextUnit.cpp:243
TInterface::LCManualLowerBarriersMessageSent
bool LCManualLowerBarriersMessageSent
indicates that the manual LC operation message has been given, so it won't be given again
Definition: InterfaceUnit.h:1306
TInterface::SaveImageAndPrefDirsMenuItemClick
void __fastcall SaveImageAndPrefDirsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3073
TInterface::AddMinsButton
TButton * AddMinsButton
Definition: InterfaceUnit.h:181
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:57
TInterface::TTAZSaveWarningNotRequired
bool TTAZSaveWarningNotRequired
if false a warning is given when attempting to save a timetable in alphabetical order,...
Definition: InterfaceUnit.h:1362
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:895
TTextHandler::TextPtrAt
TTextItem * TextPtrAt(int Caller, int At)
return the text item at position 'At' in TextVector (carries out range checking)
Definition: TextUnit.cpp:558
TInterface::MultiplayerRailwayValid
bool MultiplayerRailwayValid(AnsiString RailwayName, char &ErrorNumber)
checks whether a railway loaded by a player is listed and available
Definition: InterfaceUnit.cpp:27800
TInterface::SpeedButton60
TSpeedButton * SpeedButton60
Definition: InterfaceUnit.h:596
TInterface::TrackInfoOnOffMenuItemClick
void __fastcall TrackInfoOnOffMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:7394
TInterface::RecoverClipboard
void RecoverClipboard(int Caller, bool &ValidResult)
Recovers clipboard as track and text vectors.
Definition: InterfaceUnit.cpp:26715
TInterface::JoinMultiplayerSessionMenuItem
TMenuItem * JoinMultiplayerSessionMenuItem
Definition: InterfaceUnit.h:704
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:892
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:916
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:395
TInterface::MPHPStartButtonClick
void __fastcall MPHPStartButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27355
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:262
TInterface::AZOrderButtonClick
void __fastcall AZOrderButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:6637
TInterface::CopySelected
bool CopySelected
used to indicate whether copy or cut selected in edit menu - for clipboard pasting
Definition: InterfaceUnit.h:1288
TInterface::HiddenScreen
TImage * HiddenScreen
a hidden copy of the railway display screen used during ClearandRebuildRailway (see below) to avoid f...
Definition: InterfaceUnit.h:1509
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9791
TInterface::LoadPerformanceFile
void LoadPerformanceFile(int Caller, std::ifstream &InFile)
Load the performance file part of a sessionfile.
Definition: InterfaceUnit.cpp:25036
API::write_string
void write_string(const AnsiString &label, const AnsiString &data)
write a string directly to the metadata
Definition: API.cpp:32
TInterface::MPCPPlayerNameEditBox
TEdit * MPCPPlayerNameEditBox
Definition: InterfaceUnit.h:709
TInterface::CPCancelButtonClick
void __fastcall CPCancelButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17495
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:101
TInterface::OutputLog4MouseDown
void __fastcall OutputLog4MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16201
TInterface::AddGraphic
@ AddGraphic
Definition: InterfaceUnit.h:1065
TInterface::SetLevel1Mode
void SetLevel1Mode(int Caller)
Sets the Level1 user mode, using the Level1Mode variable to determine the mode.
Definition: InterfaceUnit.cpp:18467
TInterface::NewHomeButtonClick
void __fastcall NewHomeButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11682
TInterface::OutputLog4
TLabel * OutputLog4
Definition: InterfaceUnit.h:343
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:714
TInterface::FontDialog
TFontDialog * FontDialog
font change dialog
Definition: InterfaceUnit.h:528
TInterface::MPHPStartButton
TButton * MPHPStartButton
Definition: InterfaceUnit.h:720
TInterface::TwoLocationNamePanelHide
bool TwoLocationNamePanelHide
true if user opts not to show the two location name warning (false on starting the program)
Definition: InterfaceUnit.h:1368
TInterface::TrackSelecting
@ TrackSelecting
Definition: InterfaceUnit.h:1066
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:877
TInterface::Level2PrefDirMode
enum TInterface::TLevel2PrefDirMode Level2PrefDirMode
TInterface::OperatingPanelLabel
TLabel * OperatingPanelLabel
displays 'Operation' or 'Disabled' on the operating panel during operation for running or paused
Definition: InterfaceUnit.h:369
TInterface::DivergingPointVectorPosition
int DivergingPointVectorPosition
Definition: InterfaceUnit.h:1449
TUtilities::MinorSignalChangeEventsPerFailure
int MinorSignalChangeEventsPerFailure
Definition: Utilities.h:61
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:18103
TInterface::SpeedButton108
TSpeedButton * SpeedButton108
Definition: InterfaceUnit.h:644
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1522
TTrack::NamedLocationElementAt
bool NamedLocationElementAt(int Caller, int HLoc, int VLoc)
True if the active or inactive TrackElement at HLoc & VLoc has its FixedNamedLocationElement member t...
Definition: TrackUnit.cpp:8889
TInterface::SavePerformanceFile
void SavePerformanceFile(int Caller, std::ofstream &OutFile)
Save performance file part of a session file.
Definition: InterfaceUnit.cpp:25100
TTextItem::Font
TFont * Font
Definition: TextUnit.h:53
TInterface::MPCPPlayerNameEditBoxKeyUp
void __fastcall MPCPPlayerNameEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:28456
TAllRoutes::TLockedRouteVectorIterator
std::vector< TLockedRouteClass >::iterator TLockedRouteVectorIterator
Definition: TrackUnit.h:1685
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:513
TInterface::TTStartTimeIterator
TTEVIterator TTStartTimeIterator
Definition: InterfaceUnit.h:1529
API::dump
void dump()
save currently recorded status data to INI file
Definition: API.cpp:94
TInterface::MPHPOwnPortEditBox
TEdit * MPHPOwnPortEditBox
Definition: InterfaceUnit.h:731
TDisplay::SetFont
void SetFont(TFont *Font)
Set the screen font to 'Font'.
Definition: DisplayUnit.h:216
TOneRoute::SetRouteFlashValues
void SetRouteFlashValues(int Caller, bool AutoSigsFlag, bool PrefDirRoute)
After a route has been selected successfully this function sets all RouteFlash (see above) values app...
Definition: TrackUnit.cpp:19591
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1674
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:385
TInterface::TTLabel13
TLabel * TTLabel13
Definition: InterfaceUnit.h:393
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:233
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:229
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:884
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:302
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:107
TInterface::AZOrderButton
TButton * AZOrderButton
Definition: InterfaceUnit.h:180
TInterface::SpeedButton86
TSpeedButton * SpeedButton86
Definition: InterfaceUnit.h:622
TInterface::SpeedButton122
TSpeedButton * SpeedButton122
Definition: InterfaceUnit.h:658
Crossover
@ Crossover
Definition: TrackUnit.h:66
TTrack::SetLinkedManualLCs
void SetLinkedManualLCs(int Caller, int HLoc, int VLoc)
Set all TypeOfRoute values to 2 for all linked LCs to indicate manually lowered.
Definition: TrackUnit.cpp:6563
TInterface::SpeedButton104
TSpeedButton * SpeedButton104
Definition: InterfaceUnit.h:640
TInterface::SignallerJoinedByMenuItem
TMenuItem * SignallerJoinedByMenuItem
Definition: InterfaceUnit.h:510
TRailGraphics::SpeedBut69NormBlackGlyph
Graphics::TBitmap * SpeedBut69NormBlackGlyph
Definition: GraphicUnit.h:1086
TTrack::IsLCBarrierFlashingAtHV
bool IsLCBarrierFlashingAtHV(int Caller, int HLoc, int VLoc)
True if barrier is in process of opening or closing at H & V.
Definition: TrackUnit.cpp:7568
TDisplay::ResetZoomInOffsets
void ResetZoomInOffsets()
Reset the zoomed-in screen display to the 'Home' position.
Definition: DisplayUnit.h:202
TTrack::IsPlatformOrNamedNonStationLocationPresent
bool IsPlatformOrNamedNonStationLocationPresent(int Caller, int HLoc, int VLoc)
True if a non-station named location or platform at HLoc & VLoc.
Definition: TrackUnit.cpp:10555
TInterface::RotRightMenuItem
TMenuItem * RotRightMenuItem
Definition: InterfaceUnit.h:508
AtLocation
@ AtLocation
Definition: TrainUnit.h:76
TInterface::ExportTTButton
TButton * ExportTTButton
Definition: InterfaceUnit.h:188
Signal
@ Signal
Definition: TrackUnit.h:76
TPrefDirElement::GetSignedIntTrackVectorPosition
int GetSignedIntTrackVectorPosition() const
Returns signed integer value of TrackVectorPosition (used in flip, mirror etc for pref dirs) added at...
Definition: TrackUnit.h:311
TInterface::TimetableChangedFlag
bool TimetableChangedFlag
true when a timetable in the editor has changed (used to warn user if opts to exit without saving)
Definition: InterfaceUnit.h:1376
TInterface::SaveTimetableToErrorFile
bool SaveTimetableToErrorFile(int Caller, std::ofstream &ErrorFile, AnsiString ErrorFileStr, AnsiString TimetableFileName)
Called when compiling the error log file, to save the loaded timetable if any and the timetable being...
Definition: InterfaceUnit.cpp:24037
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:797
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:20160
TInterface::TrainHeadCodeMenuItem
TMenuItem * TrainHeadCodeMenuItem
Definition: InterfaceUnit.h:491
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
Definition: TrainUnit.h:930
TOnePrefDir::CheckOnePrefDir
bool CheckOnePrefDir(int Caller, int NumberOfActiveElements, std::ifstream &VecFile)
Called before PrefDir loading as part of the FileIntegrityCheck function in case there is an error in...
Definition: TrackUnit.cpp:13732
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1675
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:485
TPrefDirElement::SetEntryDirectionGraphicPtr
void SetEntryDirectionGraphicPtr(Graphics::TBitmap *input)
Used in pasting pref dirs.
Definition: TrackUnit.h:376
Exited
@ Exited
Definition: TrainUnit.h:92
TInterface::OutputLog2
TLabel * OutputLog2
Definition: InterfaceUnit.h:341
TInterface::TTClockAdjustWarningLabel
TLabel * TTClockAdjustWarningLabel
Definition: InterfaceUnit.h:270
TInterface::SpeedButton74
TSpeedButton * SpeedButton74
Definition: InterfaceUnit.h:610
TInterface::ScreenRightButtonClick
void __fastcall ScreenRightButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11305
TInterface::TServiceInfo::ServiceReference
AnsiString ServiceReference
Definition: InterfaceUnit.h:1125
TInterface::PositionalPanel
TPanel * PositionalPanel
new v2.2.0 panel housing the OAListBox with list of trains and times to act
Definition: InterfaceUnit.h:425
TInterface::StepForwardMenuItemClick
void __fastcall StepForwardMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:15290
TInterface::TRlyUserInfo::RailwayName
AnsiString RailwayName
Definition: InterfaceUnit.h:1105
TInterface::ErrorLog
void ErrorLog(int Caller, AnsiString Message)
The error logging routine, called when an error is detected.
Definition: InterfaceUnit.cpp:21931
TInterface::SpeedButton50
TSpeedButton * SpeedButton50
Definition: InterfaceUnit.h:586
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:11235
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:343
TInterface::StopFlashFlag
bool StopFlashFlag
true to stop stopped at signal warning image from flashing
Definition: InterfaceUnit.h:1352
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:17801
TTrain::SelReminderString
UnicodeString SelReminderString
the selected timetable string when setting reminders
Definition: TrainUnit.h:548
TInterface::NoDelaysMenuItem
TMenuItem * NoDelaysMenuItem
Definition: InterfaceUnit.h:746
TInterface::ConvertIDToPair
bool ConvertIDToPair(AnsiString HVID, THVShortPair &HVPair)
Definition: InterfaceUnit.cpp:27743
TInterface::SpeedButton31
TSpeedButton * SpeedButton31
Definition: InterfaceUnit.h:567
TInterface::UserGraphicFoundFlag
bool UserGraphicFoundFlag
indicates that a user graphic item has been found when clicking on a build screen for moving
Definition: InterfaceUnit.h:1374
TRailGraphics::CouplingExit8
Graphics::TBitmap * CouplingExit8
Definition: GraphicUnit.h:562
TInterface::RouteCancelButtonClick
void __fastcall RouteCancelButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2473
TInterface::SpeedButton91
TSpeedButton * SpeedButton91
Definition: InterfaceUnit.h:627
TUtilities::CheckAndReadOneLineFromConfigFile
bool CheckAndReadOneLineFromConfigFile(std::ifstream &InFile, AnsiString &OutString)
similar to CheckAndReadFileString but allows tab characters and doesn't ignore initial characters
Definition: Utilities.cpp:605
TInterface::SelectedTrainID
int SelectedTrainID
used to store the train ID when right clicked for signaller control actions
Definition: InterfaceUnit.h:1464
TInterface::SpeedButton113
TSpeedButton * SpeedButton113
Definition: InterfaceUnit.h:649
TInterface::SpeedButton82
TSpeedButton * SpeedButton82
Definition: InterfaceUnit.h:618
TInterface::MultiplayerHostSessionMenuItemClick
void __fastcall MultiplayerHostSessionMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27174
TTextHandler::SelectTextPtrAt
TTextItem * SelectTextPtrAt(int Caller, int At)
return the text item at position 'At' in SelectTextVector (carries out range checking)
Definition: TextUnit.cpp:573
TInterface::SpeedButton132
TSpeedButton * SpeedButton132
Definition: InterfaceUnit.h:668
TTrack::ChangingLCVector
TActiveLCVector ChangingLCVector
vector of values for changing level crossings - i.e. barriers in course of being raised or lowered
Definition: TrackUnit.h:803
TInterface::FlashingGraphics
void FlashingGraphics(int Caller, TDateTime Now)
Deal with any warning graphics that need to flash (call on, signal stop, crash etc),...
Definition: InterfaceUnit.cpp:21225
TInterface::ScreenLeftButton
TBitBtn * ScreenLeftButton
Definition: InterfaceUnit.h:290
TInterface::CheckPrefDirConflictsMenuItem
TMenuItem * CheckPrefDirConflictsMenuItem
Definition: InterfaceUnit.h:476
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:513
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:804
TInterface::TTClockx16ButtonClick
void __fastcall TTClockx16ButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16876
TInterface::BuildTrackMenuItem
TMenuItem * BuildTrackMenuItem
Definition: InterfaceUnit.h:454
TInterface::SaveOperatingImageMenuItem
TMenuItem * SaveOperatingImageMenuItem
Definition: InterfaceUnit.h:486
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:848
TInterface::NoRailway
bool NoRailway()
Returns true if there are no track elements and no text.
Definition: InterfaceUnit.cpp:26202
TInterface::AllEntriesTTListBox
TListBox * AllEntriesTTListBox
Operator action list, sits inside ActionsDuePanel and lists trains in ascending order of time to act.
Definition: InterfaceUnit.h:440
TInterface::MultiplayerHostStringGrid
TStringGrid * MultiplayerHostStringGrid
Definition: InterfaceUnit.h:726
TInterface::CallingOnButtonClick
void __fastcall CallingOnButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11213
TInterface::SaveTTDialog
TSaveDialog * SaveTTDialog
Definition: InterfaceUnit.h:527
TInterface::DeleteOnePrefDirButtonClick
void __fastcall DeleteOnePrefDirButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2171
TAllRoutes::NextRouteID
int NextRouteID
stores the value for the route ID number that is next to be built
Definition: TrackUnit.h:1742
TUtilities::CheckAndReadFileString
bool CheckAndReadFileString(std::ifstream &InFile, AnsiString &OutString)
checks that the value is a string ('0' or ' ' (CRLF) accepted as delimiters), returns true for succes...
Definition: Utilities.cpp:553
TInterface::TTEntryChangedFlag
bool TTEntryChangedFlag
true when a timetable entry that is displayed in the timetable entry edit window has changed
Definition: InterfaceUnit.h:1386
TInterface::HighlightPanel
TPanel * HighlightPanel
the orange bar that displays the current timetable entry in AllEntriesTTListBox
Definition: InterfaceUnit.h:413
TInterface::SpeedButton16
TSpeedButton * SpeedButton16
Definition: InterfaceUnit.h:552
TInterface::MoveTTEntryUpButtonClick
void __fastcall MoveTTEntryUpButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5123
TInterface::SpeedBottomLabel
TLabel * SpeedBottomLabel
Definition: InterfaceUnit.h:221
TInterface::GetTrainIDOrContinuationPosition
bool GetTrainIDOrContinuationPosition(int Caller, int X, int Y, int &TrainID, int &TrackVectorPosition)
Used in actions due panel to identify the train or continuation, added at v2.6.2 so can use for actio...
Definition: InterfaceUnit.cpp:27110
TInterface::CheckPrefDirConflictsMenuItemClick
void __fastcall CheckPrefDirConflictsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12988
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:860
TUserGraphicItem::FileName
AnsiString FileName
Definition: DisplayUnit.h:34
TInterface::TTClockResetButtonClick
void __fastcall TTClockResetButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17051
TInterface::ClockLabel
TLabel * ClockLabel
the timetable clock
Definition: InterfaceUnit.h:367
TInterface::SpeedButton43
TSpeedButton * SpeedButton43
Definition: InterfaceUnit.h:579
TOneRoute::GetNextPreferredRouteElement
bool GetNextPreferredRouteElement(int Caller, int HLoc, int VLoc, TOnePrefDir *EveryPrefDir, bool ConsecSignals, bool AutoSigsFlag, IDInt &ReqPosRouteID, bool &PointsChanged)
Try to find a set of linked tracks that lie on preferred directions between the route start element a...
Definition: TrackUnit.cpp:15632
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:381
TInterface::SetTrackBuildImages
void SetTrackBuildImages(int Caller)
Sets the left screen images (track linked or not, gaps set or not, locations named or not) during rai...
Definition: InterfaceUnit.cpp:22203
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:459
TTrack::WriteGraphicsToImage
void WriteGraphicsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by SaveImageNoGridMenuItemClick, SaveImageAndGridMenuItemClick amd SaveImageAndPrefDirsMenuIte...
Definition: TrackUnit.cpp:4233
TInterface::AddText
@ AddText
Definition: InterfaceUnit.h:1065
TTrack::PlotPastedTrackElementWithAttributes
void PlotPastedTrackElementWithAttributes(int Caller, TTrackElement TempTrackElement, int HLocInput, int VLocInput, bool &TrackLinkingRequiredFlag, bool InternalChecks)
new at v2.2.0 - as PlotAndAddTrackElement but keeping speed & length attributes (for pasting) and als...
Definition: TrackUnit.cpp:2419
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:903
TInterface::LoadTimetableMenuItemClick
void __fastcall LoadTimetableMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:13629
TTrack::RebuildLocationNameMultiMap
void RebuildLocationNameMultiMap(int Caller)
Clears the existing LocationNameMultiMap and rebuilds it from TrackVector and InactiveTrackVector....
Definition: TrackUnit.cpp:9761
TInterface::LengthEditKeyUp
void __fastcall LengthEditKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:16658
TTrack::PlotAndAddTrackElement
void PlotAndAddTrackElement(int Caller, int CurrentTag, int Aspect, int HLocInput, int VLocInput, bool &TrackPlottedFlag, bool InternalChecks, bool PerformNameSearch)
Called during track building or pasting, when an element identified by CurrentTag (i....
Definition: TrackUnit.cpp:2161
TOnePrefDir::EveryPrefDirMarker
void EveryPrefDirMarker(int Caller, TDisplay *Disp)
Similar to PrefDirMarker but used only to display EveryPrefDir - red for unidirectional PrefDir & gre...
Definition: TrackUnit.cpp:13500
TInterface::OutputLog1
TLabel * OutputLog1
Definition: InterfaceUnit.h:340
TInterface::PopulateCouplingMap
bool PopulateCouplingMap(AnsiString FileName, int &NumExt)
Read couplingfile and convert into CouplingMap.
Definition: InterfaceUnit.cpp:27557
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:795
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:411
TInterface::TServiceInfo::RepeatNumber
short RepeatNumber
Definition: InterfaceUnit.h:1126
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3559
TInterface::SpeedButton84
TSpeedButton * SpeedButton84
Definition: InterfaceUnit.h:620
TInterface::SpeedButton77
TSpeedButton * SpeedButton77
Definition: InterfaceUnit.h:613
TInterface::SpeedButton128
TSpeedButton * SpeedButton128
Definition: InterfaceUnit.h:664
TInterface::GetTrainFloatingInfoFromContinuation
void GetTrainFloatingInfoFromContinuation(int Caller, int VecPos, AnsiString FormatNoDPStr, AnsiString SpecialStr, AnsiString &TrainStatusFloat, AnsiString &TrainTTFloat)
Called when floating train info needed and train hasn't entered yet.
Definition: InterfaceUnit.cpp:20823
TTrack::SetTrackFinished
void SetTrackFinished(bool Value)
Definition: TrackUnit.h:968
TTrack::GetInactiveTrackElementFromTrackMap
TTrackElement & GetInactiveTrackElementFromTrackMap(int Caller, int HLoc, int VLoc)
Return a reference to the inactive element at HLoc & VLoc, if no element is found an error is thrown.
Definition: TrackUnit.cpp:5927
TInterface::AddSubMinsBox
TEdit * AddSubMinsBox
the edit box that accepts minutes to add or subtract
Definition: InterfaceUnit.h:215
TAllRoutes::LoadRoutes
bool LoadRoutes(int Caller, std::ifstream &InFile)
Loads the routes from a session file.
Definition: TrackUnit.cpp:21373
TInterface::TTClockExitButton
TButton * TTClockExitButton
Definition: InterfaceUnit.h:258
FNil
@ FNil
Definition: Utilities.h:43
TInterface::SpeedEditBox2
TEdit * SpeedEditBox2
Definition: InterfaceUnit.h:141
API::add_metadata_int
void add_metadata_int(const AnsiString &label, int *data)
add pointer to integer variable to monitor value
Definition: API.cpp:28
TInterface::MoveForwardsMenuItemClick
void __fastcall MoveForwardsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:13976
TTrack::Down
@ Down
Definition: TrackUnit.h:614
TUtilities::LastTSRCheckTime
TDateTime LastTSRCheckTime
time of last TSR check, used every minute, added at v2.13.0 //change from 5 mins to 1 min at v2....
Definition: Utilities.h:115
TInterface::TimetableNameLabel
TLabel * TimetableNameLabel
displays the current timetable name on the timetable edit panel
Definition: InterfaceUnit.h:357
TInterface::SpeedButton79
TSpeedButton * SpeedButton79
Definition: InterfaceUnit.h:615
TInterface::SpeedBottomLabel2
TLabel * SpeedBottomLabel2
Definition: InterfaceUnit.h:152
TInterface::SpeedButton51
TSpeedButton * SpeedButton51
Definition: InterfaceUnit.h:587
API::reset_all
void reset_all()
reset all variables
Definition: API.cpp:131
TInterface::BuildOneRailwayCouplingMap
short BuildOneRailwayCouplingMap(unsigned char PlayerNumber)
store coupling map for a given railway name, return number of bytes needed for datagram
Definition: InterfaceUnit.cpp:27835
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:70
TOneRoute::ReqPosRouteID
IDInt ReqPosRouteID
session saves as routes in build are not saved in sessions
Definition: TrackUnit.h:1556
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:473
TInterface::TimetableChangedInAZOrderFlag
bool TimetableChangedInAZOrderFlag
used to give a warning message that changes will be discarded if proceed
Definition: InterfaceUnit.h:1380
TTrack::PlotRaisedLinkedLevelCrossingBarriers
void PlotRaisedLinkedLevelCrossingBarriers(int Caller, int BaseElementSpeedTag, int HLoc, int VLoc, TDisplay *Disp)
Plot & close (to trains) all level crossings linked to TrackElement - always plots as red - auto.
Definition: TrackUnit.cpp:7266
TInterface::AddTrackButtonClick
void __fastcall AddTrackButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:897
TInterface::SpeedButton92
TSpeedButton * SpeedButton92
Definition: InterfaceUnit.h:628
TInterface::DeleteMenuItemClick
void __fastcall DeleteMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12830
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:513
TInterface::ConflictAnalysisButton
TButton * ConflictAnalysisButton
Definition: InterfaceUnit.h:273
TInterface::MMoveTextGraphicUserGraphicFoundFlag
bool MMoveTextGraphicUserGraphicFoundFlag
Mouse move flags to prevent repeated event logs.
Definition: InterfaceUnit.h:1318
TTextHandler::TextVectorPush
void TextVectorPush(int Caller, TTextItem Text)
push &Text onto TextVector & reset the size of the railway if necessary
Definition: TextUnit.cpp:508
TInterface::TTClockAdjButton
TBitBtn * TTClockAdjButton
Definition: InterfaceUnit.h:244
TInterface::PowerEditBoxKeyUp
void __fastcall PowerEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:16523
TInterface::MPCPHostIPEditBoxKeyUp
void __fastcall MPCPHostIPEditBoxKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:28361
TOneRoute::TRouteFlash::PlotRouteOriginal
void PlotRouteOriginal(int Caller)
display the original (non route-coloured) graphic
Definition: TrackUnit.cpp:19683
TTrack::TTrackMapEntry
std::pair< THVPair, unsigned int > TTrackMapEntry
Definition: TrackUnit.h:663
TTrack::ResetLevelCrossings
void ResetLevelCrossings(int Caller)
Set all LC attributes to 0 (closed to trains)
Definition: TrackUnit.cpp:7641
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:17850
API::add_metadata_str
void add_metadata_str(const AnsiString &label, AnsiString *data)
add pointer to string variable to monitor value
Definition: API.cpp:20
TInterface::OpenHelpMenuItemClick
void __fastcall OpenHelpMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16291
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:467
TInterface::ModerateFailuresMenuItem
TMenuItem * ModerateFailuresMenuItem
Definition: InterfaceUnit.h:753
TInterface::CheckPerformanceFile
bool CheckPerformanceFile(int Caller, std::ifstream &InFile)
Check the performance file embedded within a session file & return false for error,...
Definition: InterfaceUnit.cpp:25069
TInterface::TInterface
__fastcall TInterface(TComponent *Owner)
constructor
Definition: InterfaceUnit.cpp:86
TInterface::SpeedButton15
TSpeedButton * SpeedButton15
Definition: InterfaceUnit.h:551
TInterface::HeatmapsRedlowvaluesMenuItem
TMenuItem * HeatmapsRedlowvaluesMenuItem
Definition: InterfaceUnit.h:770
clB4G5R5
#define clB4G5R5
Definition: GraphicUnit.h:245
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6745
TInterface::TooLongMessageSentFlag
bool TooLongMessageSentFlag
indicates that the length of a location element might be too long (>200m), so it won't be given again
Definition: InterfaceUnit.h:1370
TInterface::ReminderListBoxMouseUp
void __fastcall ReminderListBoxMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:14644
Timetable
@ Timetable
Definition: TrainUnit.h:64
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:16867
TInterface
Definition: InterfaceUnit.h:94
StartSequence
@ StartSequence
Definition: TrainUnit.h:81
TTextHandler::SetFontStyleFromInt
TFontStyles SetFontStyleFromInt(int Caller, int Input)
used in loading from a file
Definition: TextUnit.cpp:129
TInterface::RemoveTrainMenuItemClick
void __fastcall RemoveTrainMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:15353
TInterface::RotRightMenuItemClick
void __fastcall RotRightMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12239
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7400
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:854
TInterface::SaveInterface
void SaveInterface(int Caller, std::ofstream &SessionFile)
Save interface part of a session file.
Definition: InterfaceUnit.cpp:23614
TTrack::TActiveLevelCrossing::TypeOfRoute
int TypeOfRoute
route type - 0 = nonsignals, 1 = preferred direction (can't have autosigs), 2 no route,...
Definition: TrackUnit.h:621
TInterface::AllRailwaysCouplingPair
TCouplingPair AllRailwaysCouplingPair
Definition: InterfaceUnit.h:1153
Parapet
@ Parapet
Definition: TrackUnit.h:67
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:787
HiddenDisplay
TDisplay * HiddenDisplay
The object pointer for the internal hidden display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:55
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:19832
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:121
TInterface::RouteFlashDuration
float RouteFlashDuration
duration of the route flash period
Definition: InterfaceUnit.h:1425
TInterface::SpeedButton4
TSpeedButton * SpeedButton4
Definition: InterfaceUnit.h:540
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:846
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:275
TTrack::CheckLocationNameMultiMap
void CheckLocationNameMultiMap(int Caller)
Validity test.
Definition: TrackUnit.cpp:9430
TOneRoute::TRouteFlash::PlotRouteOverlay
void PlotRouteOverlay(int Caller)
display the overlay (route-coloured) graphic
Definition: TrackUnit.cpp:19656
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:455
TInterface::SkipTimetabledActionsMenuItemClick
void __fastcall SkipTimetabledActionsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14231
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:852
TTrack::InactiveTrackVector
TTrackVector InactiveTrackVector
Definition: TrackUnit.h:829
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5777
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:801
TInterface::MPHostClient
TIdUDPClient * MPHostClient
Definition: InterfaceUnit.h:735
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:513
TInterface::SpeedButton44
TSpeedButton * SpeedButton44
Definition: InterfaceUnit.h:580
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TInterface::ScreenRightButton
TBitBtn * ScreenRightButton
Definition: InterfaceUnit.h:289
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:529
TInterface::ReminderListBox
TListBox * ReminderListBox
Definition: InterfaceUnit.h:762
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:383
TInterface::SaveTTButton
TButton * SaveTTButton
Definition: InterfaceUnit.h:185
TUtilities::CheckStringDouble
bool CheckStringDouble(AnsiString &DoubleString)
checks the string represents a valid double value, returns true for success. Added at v2....
Definition: Utilities.cpp:394
TInterface::TimeOrderButtonClick
void __fastcall TimeOrderButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:6745
TInterface::SpeedButton63
TSpeedButton * SpeedButton63
Definition: InterfaceUnit.h:599
TRailGraphics::bmLightBlueRect
Graphics::TBitmap * bmLightBlueRect
Definition: GraphicUnit.h:527
TInterface::MPHPLoadCouplingFileButtonClick
void __fastcall MPHPLoadCouplingFileButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27223
TInterface::NonSigRouteStartMarker
TGraphicElement * NonSigRouteStartMarker
Definition: InterfaceUnit.h:1503
clB3G3R3
#define clB3G3R3
Definition: GraphicUnit.h:187
TInterface::SpeedButton53
TSpeedButton * SpeedButton53
Definition: InterfaceUnit.h:589
TInterface::SpeedButton107
TSpeedButton * SpeedButton107
Definition: InterfaceUnit.h:643
TUtilities::MTBTSRs
double MTBTSRs
temporary speed restriction, units /day/simple element added at v2.13.0
Definition: Utilities.h:93
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:18126
TInterface::EditMenuClick
void __fastcall EditMenuClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11710
TTrackElement::LCPlotted
bool LCPlotted
Utility marker to avoid plotting every element of a multitrack LC during ClearandRebuildRailway.
Definition: TrackUnit.h:137
TInterface::TrackInfoShowing
bool TrackInfoShowing
Definition: InterfaceUnit.h:1170
Nil
@ Nil
Definition: Utilities.h:38
TOnePrefDir::PrefDirMarker
void PrefDirMarker(int Caller, TPrefDirRoute PrefDirRoute, bool BuildingPrefDir, TDisplay *Disp) const
PrefDir and route track display function, including direction markers.
Definition: TrackUnit.cpp:13427
TInterface::ModeMenu
TMenuItem * ModeMenu
Definition: InterfaceUnit.h:453
TInterface::SpeedButton112
TSpeedButton * SpeedButton112
Definition: InterfaceUnit.h:648
TInterface::MTBFEditBoxClick
void __fastcall MTBFEditBoxClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17337
TInterface::ExpandRepeatsButton
TButton * ExpandRepeatsButton
Definition: InterfaceUnit.h:759
TInterface::MasterClock
TTimer * MasterClock
the program clock (not the timetable clock)
Definition: InterfaceUnit.h:531
TInterface::OutputLog6MouseDown
void __fastcall OutputLog6MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16220
TRailGraphics::SpeedBut70GrndBlackGlyph
Graphics::TBitmap * SpeedBut70GrndBlackGlyph
Definition: GraphicUnit.h:1095
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:904
TInterface::CreateEditTTTitle
AnsiString CreateEditTTTitle
the title of the timetable currently being edited - i.e. the filename without the '....
Definition: InterfaceUnit.h:1250
TInterface::AboutMenuItemClick
void __fastcall AboutMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16270
TTrainController::TOpTimeToActMultiMapIterator
TOpTimeToActMultiMap::iterator TOpTimeToActMultiMapIterator
Definition: TrainUnit.h:836
TInterface::SpeedButton6
TSpeedButton * SpeedButton6
Definition: InterfaceUnit.h:542
TTrack
Definition: TrackUnit.h:551
TRailGraphics::CouplingExit6
Graphics::TBitmap * CouplingExit6
Definition: GraphicUnit.h:560
TTrack::TwoAspectBuild
@ TwoAspectBuild
Definition: TrackUnit.h:876
TInterface::PlayerFiveSecondTimer
int PlayerFiveSecondTimer
Definition: InterfaceUnit.h:1191
TInterface::OperateRailwayMenuItem
TMenuItem * OperateRailwayMenuItem
Definition: InterfaceUnit.h:458
TInterface::SaveRailwayTBPButton
TBitBtn * SaveRailwayTBPButton
Save button on TrackBuildPanel.
Definition: InterfaceUnit.h:111
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:441
TInterface::MTBFLabel
TLabel * MTBFLabel
Definition: InterfaceUnit.h:514
TOnePrefDir::PrefDirVector
TPrefDirVector PrefDirVector
Definition: TrackUnit.h:1414
TInterface::SelectBitmap
Graphics::TBitmap * SelectBitmap
the graphic defined by Edit->Select & Edit->Reselect
Definition: InterfaceUnit.h:1430
TInterface::PreventGapOffsetResetting
bool PreventGapOffsetResetting
during gap setting gaps are highlighted in turn for the user to select the matching gap,...
Definition: InterfaceUnit.h:1332
TInterface::TakeSignallerControlMenuItem
TMenuItem * TakeSignallerControlMenuItem
Definition: InterfaceUnit.h:492
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:287
TInterface::Pasting
@ Pasting
Definition: InterfaceUnit.h:1066
TInterface::AddTextButton
TBitBtn * AddTextButton
Definition: InterfaceUnit.h:104
TInterface::LoadRailwayMenuItem
TMenuItem * LoadRailwayMenuItem
Definition: InterfaceUnit.h:444
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TInterface::CrashImage
TImage * CrashImage
Definition: InterfaceUnit.h:305
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:920
TInterface::SpeedButton123
TSpeedButton * SpeedButton123
Definition: InterfaceUnit.h:659
TInterface::USERGRAPHICS_DIR_NAME
static const UnicodeString USERGRAPHICS_DIR_NAME
Definition: InterfaceUnit.h:1051
TInterface::mbLeftDown
bool mbLeftDown
true when the left mouse button is down
Definition: InterfaceUnit.h:1312
TInterface::ShowActionsDueForm
bool ShowActionsDueForm
true when the 'trains needing action' button has been clicked during operation (new at v2....
Definition: InterfaceUnit.h:1384
TInterface::SpeedButton115
TSpeedButton * SpeedButton115
Definition: InterfaceUnit.h:651
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:7225
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:914
TInterface::ProgramVersion
UnicodeString ProgramVersion
Definition: InterfaceUnit.h:1019
TInterface::NoFailuresMenuItemClick
void __fastcall NoFailuresMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27030
TInterface::TTClockResetButton
TButton * TTClockResetButton
Definition: InterfaceUnit.h:259
TInterface::SpeedHeatmapBitBtn
TBitBtn * SpeedHeatmapBitBtn
Definition: InterfaceUnit.h:122
TTrain::Description
AnsiString Description
needs own HeadCode & description because repeat entries will differ from TrainDataEntry values (Descr...
Definition: TrainUnit.h:337
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:304
TInterface::TempCursor
TCursor TempCursor
stores the screen cursor while a temporary cursor (ususlly an hourglass) is displayed
Definition: InterfaceUnit.h:1493
Interface
TInterface * Interface
Definition: InterfaceUnit.cpp:71
TInterface::SpeedButton61
TSpeedButton * SpeedButton61
Definition: InterfaceUnit.h:597
TInterface::ExitSimulationMenuItem
TMenuItem * ExitSimulationMenuItem
Definition: InterfaceUnit.h:702
TDisplay::HideWarningLog
void HideWarningLog(int Caller)
Hide all the warnings from the top part of the screen - for timetable clock adjustment.
Definition: DisplayUnit.cpp:549
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:128
TUtilities::DecimalPoint
char DecimalPoint
<if false signals facing bridges are not permitted, but can be set to true using CTRL ALT 5
Definition: Utilities.h:89
TInterface::BuildDynamicMapFromHostDatagram
bool BuildDynamicMapFromHostDatagram(int Caller, int TTTime, TDynamicMap &DMap, TBytes Buffer)
converse of BuildDatagramFromHostMap
Definition: InterfaceUnit.cpp:28719
TInterface::AllRailwaysCouplingMap
TCouplingMap AllRailwaysCouplingMap
Definition: InterfaceUnit.h:1154
TInterface::ConstructPrefDir
TOnePrefDir * ConstructPrefDir
the Pref Dir under construction
Definition: InterfaceUnit.h:1514
TInterface::SpeedButton81
TSpeedButton * SpeedButton81
Definition: InterfaceUnit.h:617
TInterface::SigsOnLeftImage1
TImage * SigsOnLeftImage1
Definition: InterfaceUnit.h:328
TInterface::BuildTrackMenuItemClick
void __fastcall BuildTrackMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:880
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:123
TTrack::LoadTrack
void LoadTrack(int Caller, std::ifstream &VecFile, bool &GraphicsFollow)
Load track elements (active & inactive) from the file into the relevant vectors and maps,...
Definition: TrackUnit.cpp:3038
TInterface::ClearandRebuildRailway
void ClearandRebuildRailway(int Caller)
< Sets the information panel message for zoom-out or paused modes
Definition: InterfaceUnit.cpp:17922
TInterface::DisplayOneTTLineInPanel
void DisplayOneTTLineInPanel(int Caller, AnsiString Data, bool ServiceEntry)
Display a line from the TimetableEditVector (consists of a series of AnsiStrings, each of which repre...
Definition: InterfaceUnit.cpp:7229
TInterface::PlanPrefDirsMenuItem
TMenuItem * PlanPrefDirsMenuItem
Definition: InterfaceUnit.h:455
TInterface::AreAnyTimesInCurrentEntry
bool AreAnyTimesInCurrentEntry()
Search the timetable entry pointed to by TTCurrentEntryPtr and if any times (HH:MM) are present retur...
Definition: InterfaceUnit.cpp:7321
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:10323
TInterface::AutoRouteStartMarker
TGraphicElement * AutoRouteStartMarker
Definition: InterfaceUnit.h:1503
TInterface::RestoreTTButtonClick
void __fastcall RestoreTTButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5284
TInterface::SaveTTButtonClick
void __fastcall SaveTTButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4827
TInterface::FormCreate
void __fastcall FormCreate(TObject *Sender)
Definition: InterfaceUnit.cpp:767
TGraphicElement::SetSourceRect
void SetSourceRect(int Left, int Top)
Set SourceRect member values from those supplied and existing Width & Height - ensure this is only ca...
Definition: TrackUnit.h:461
TTrack::SelectPush
void SelectPush(TTrackElement TrackElement)
Store a TrackElement in the SelectVector.
Definition: TrackUnit.h:947
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:492
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1748
TInterface::SpeedButton7
TSpeedButton * SpeedButton7
Definition: InterfaceUnit.h:543
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:860
TInterface::TimetableChangedInTimeOrderFlag
bool TimetableChangedInTimeOrderFlag
used to give a warning message that changes will be discarded if proceed
Definition: InterfaceUnit.h:1382
TInterface::TimetablePanel
TPanel * TimetablePanel
'Create a timetable'/'Edit a timetable' panel that contains the topmost buttons (show/hide & exit)
Definition: InterfaceUnit.h:404
TInterface::OneRailwayCouplingMap
TCouplingMap OneRailwayCouplingMap
Definition: InterfaceUnit.h:1154
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:4058
TInterface::LoadSessionMenuItem
TMenuItem * LoadSessionMenuItem
Definition: InterfaceUnit.h:448
TInterface::SpeedButton69
TSpeedButton * SpeedButton69
Definition: InterfaceUnit.h:605
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:159
TInterface::OutputLog5MouseDown
void __fastcall OutputLog5MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16211
TInterface::ErrorButton
TBitBtn * ErrorButton
the 'Press to exit' button on the error screen
Definition: InterfaceUnit.h:297
TInterface::SpeedButton24
TSpeedButton * SpeedButton24
Definition: InterfaceUnit.h:560
TInterface::WarningFlash
bool WarningFlash
toggles on and off automatically at a cycle of about 0.5 sec, used to drive the warning icons during ...
Definition: InterfaceUnit.h:1388
TInterface::CurrentSpeedButton
TSpeedButton * CurrentSpeedButton
stores the selected track build element button during railway building
Definition: InterfaceUnit.h:1526
TInterface::MainScreenMouseDown
void __fastcall MainScreenMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:7471
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:485
TTrackElement::TwoAspect
@ TwoAspect
Definition: TrackUnit.h:161
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:809
TInterface::TotalTicks
unsigned int TotalTicks
total clock ticks
Definition: InterfaceUnit.h:1436
TTrack::TryToConnectTrack
bool TryToConnectTrack(int Caller, bool &LocError, int &HLoc, int &VLoc, bool GiveMessages)
Handles all tasks associated with track linking, returns true if successful (see also LinkTrack & Lin...
Definition: TrackUnit.cpp:2639
TInterface::SpeedTopLabel2
TLabel * SpeedTopLabel2
Definition: InterfaceUnit.h:151
TInterface::AnsiCouplingMap
TAnsiCouplingMap AnsiCouplingMap
Definition: InterfaceUnit.h:1090
TInterface::MMoveTextGraphicTextFoundFlag
bool MMoveTextGraphicTextFoundFlag
Definition: InterfaceUnit.h:1317
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:57
TRailGraphics::GridBitmap
Graphics::TBitmap * GridBitmap
Definition: GraphicUnit.h:937
TInterface::PowerBottomLabel
TLabel * PowerBottomLabel
Definition: InterfaceUnit.h:229
TInterface::PasteMenuItem
TMenuItem * PasteMenuItem
Definition: InterfaceUnit.h:471
TInterface::TRlyUserInfo
Definition: InterfaceUnit.h:1102
TTextHandler::EnterAndDisplayNewText
void EnterAndDisplayNewText(int Caller, TTextItem Text, int HPos, int VPos)
add Text to TextVector and display it on the screen
Definition: TextUnit.cpp:193
TInterface::MPPlayerClient
TIdUDPClient * MPPlayerClient
Definition: InterfaceUnit.h:695
TOnePrefDir::EndPossible
bool EndPossible(int Caller, bool &LeadingPoints)
Used when setting preferred directions, true if able to finish at the last selected element (can't fi...
Definition: TrackUnit.cpp:13215
TInterface::CreateEditTTFileName
AnsiString CreateEditTTFileName
the full path and filename of the timetable file
Definition: InterfaceUnit.h:1248
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:123
TOnePrefDir::ConsolidatePrefDirs
void ConsolidatePrefDirs(int Caller, TOnePrefDir *InputPrefDir)
Used when a preferred direction has been set to add all the elements to EveryPrefDir,...
Definition: TrackUnit.cpp:13993
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:880
TInterface::ManualLCDownAttentionWarning
bool ManualLCDownAttentionWarning
Displays the manual LC down warning graphic in the panel on the LHS of the railway when there are no ...
Definition: InterfaceUnit.h:1587
TInterface::PerformanceLogButtonClick
void __fastcall PerformanceLogButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2495
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:17972
TInterface::HideTTActionsListBox
void HideTTActionsListBox(int Caller)
makes TTActionsListBox invisible (if it was visible)
Definition: InterfaceUnit.cpp:14821
IDInt
Definition: TrackUnit.h:500
TTrain::GetLeadElement
void GetLeadElement(int Caller)
Called when a train is about to leave an element and move onto another.
Definition: TrainUnit.cpp:3014
TInterface::SelectLengthsMenuItemClick
void __fastcall SelectLengthsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12849
TInterface::NewSelectBitmapHLoc
int NewSelectBitmapHLoc
the new (during & at end of moving) HLoc value of Edit->Select & Edit->Reselect
Definition: InterfaceUnit.h:1442
TInterface::LoadUserGraphic
void LoadUserGraphic(int Caller)
Load a user-defined graphic (bmp, gif, jpg, png).
Definition: InterfaceUnit.cpp:26449
TRunningEntry
TRunningEntry
contains status info for each train
Definition: TrainUnit.h:91
TInterface::OutputLog6
TLabel * OutputLog6
Definition: InterfaceUnit.h:345
TUtilities
Definition: Utilities.h:47
TTrack::FindAndHighlightAnUnsetGap
bool FindAndHighlightAnUnsetGap(int Caller)
True if there is an unset gap, and if so it is marked with a red circle, used during gap setting.
Definition: TrackUnit.cpp:4548
TInterface::CreateTimetableMenuItemClick
void __fastcall CreateTimetableMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3428
TInterface::ExportTTButtonClick
void __fastcall ExportTTButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5367
TDisplay
Definition: DisplayUnit.h:50
TInterface::SpeedButton9
TSpeedButton * SpeedButton9
Definition: InterfaceUnit.h:545
TInterface::TTClockxEighthButtonClick
void __fastcall TTClockxEighthButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16952
TTrack::PlotSmallRailway
void PlotSmallRailway(int Caller, TDisplay *Disp)
Plot on screen the zoomed-out railway.
Definition: TrackUnit.cpp:10922
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:922
TInterface::Text_X
int Text_X
the 'X' pixel value for an item of text
Definition: InterfaceUnit.h:1477
TInterface::SpeedVariableLabel
TLabel * SpeedVariableLabel
Definition: InterfaceUnit.h:222
TInterface::SpeedButton49
TSpeedButton * SpeedButton49
Definition: InterfaceUnit.h:585
TInterface::SigPrefNonConsecButton
TBitBtn * SigPrefNonConsecButton
Definition: InterfaceUnit.h:686
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:514
TInterface::GetTrainStatusFloat
AnsiString GetTrainStatusFloat(int Caller, int TrainID, AnsiString FormatNoDPStr, AnsiString SpecialStr)
used for floating window to display train status
Definition: InterfaceUnit.cpp:20917
TInterface::PowerVariableLabel
TLabel * PowerVariableLabel
Definition: InterfaceUnit.h:230
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:40
TInterface::ReminderExitImageClick
void __fastcall ReminderExitImageClick(TObject *Sender)
Definition: InterfaceUnit.cpp:14868
TInterface::GapsNotSetImage
TImage * GapsNotSetImage
Definition: InterfaceUnit.h:325
TInterface::Level1Mode
enum TInterface::TLevel1Mode Level1Mode
TInterface::CallLogTickerLabel
TLabel * CallLogTickerLabel
diagnostic label displaying the call log depth, made visible by ctrl+ alt+ 2
Definition: InterfaceUnit.h:365
TInterface::TimetableEditVector
TTimetableEditVector TimetableEditVector
Definition: InterfaceUnit.h:1532
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:11076
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:56
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:423
TInterface::SaveRailwayDialog
TSaveDialog * SaveRailwayDialog
Definition: InterfaceUnit.h:526
TInterface::TTClockAdjustWarningPanel
TPanel * TTClockAdjustWarningPanel
Definition: InterfaceUnit.h:253
TInterface::TServiceInfo::TServiceInfo
TServiceInfo()
Definition: InterfaceUnit.cpp:28909
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:876
TInterface::TrainTTInfoOnOffMenuItemClick
void __fastcall TrainTTInfoOnOffMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:7444
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:97
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:10337
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:141
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:899
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:906
TTrain::PowerAtRail
double PowerAtRail
< saves MinDwellTime from the arrival event so it can be retrieved at departure
Definition: TrainUnit.h:465
TTrack::FlipArray
int FlipArray[FirstUnusedSpeedTagNumber]
holds TrackElement SpeedTag values for 'flipping' via menu items 'Edit' & 'Flip'
Definition: TrackUnit.h:785
TOneRoute::GetPreferredRouteStartElement
bool GetPreferredRouteStartElement(int Caller, int HLoc, int VLoc, TOnePrefDir *EveryPrefDir, bool AutoSigsFlag)
Set the starting conditions for a preferred direction or automatic signal route selection beginning o...
Definition: TrackUnit.cpp:15429
TAllRoutes::LevelCrossingBarrierDownDelay
const float LevelCrossingBarrierDownDelay
the full value in seconds for which the level crossing flashes prior to opening to trains
Definition: TrackUnit.h:1736
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1729
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:186
TInterface::SetSaveMenuAndButtons
void SetSaveMenuAndButtons(int Caller)
Called during the ClockTimer2 function to set screen boundaries, buttons & menu items.
Definition: InterfaceUnit.cpp:21576
TTrack::SignalAspectBuildMode
enum TTrack::@2 SignalAspectBuildMode
aspect mode for future signal additions
TInterface::TIMETABLE_DIR_NAME
static const UnicodeString TIMETABLE_DIR_NAME
Definition: InterfaceUnit.h:1046
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:130
TInterface::SaveImageAndPrefDirsMenuItem
TMenuItem * SaveImageAndPrefDirsMenuItem
Definition: InterfaceUnit.h:485
TInterface::LoadRailwayMenuItemClick
void __fastcall LoadRailwayMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2627
TTrack::CheckMapAndInactiveTrack
void CheckMapAndInactiveTrack(int Caller)
Validity test.
Definition: TrackUnit.cpp:7978
TInterface::FormKeyUp
void __fastcall FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
Definition: InterfaceUnit.cpp:16159
TInterface::AZWarningSent
bool AZWarningSent
true when warning about alphabetical timetable order has been given
Definition: InterfaceUnit.h:1276
TInterface::SkipListExitImage
TImage * SkipListExitImage
Definition: InterfaceUnit.h:743
TInterface::CopyTTEntryKeyFlag
bool CopyTTEntryKeyFlag
Definition: InterfaceUnit.h:1403
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:237
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:457
TInterface::OutputLog9MouseDown
void __fastcall OutputLog9MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16250
TTrack::TActiveLevelCrossing::StartTime
TDateTime StartTime
stores the starting time for level crossing changing
Definition: TrackUnit.h:635
TInterface::RecoverClipboardMessageSent
bool RecoverClipboardMessageSent
indicates that the warning about pasting at top left in a new application has been given,...
Definition: InterfaceUnit.h:1334
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:911
TOneRoute::TRouteFlash::RouteFlashVector
std::vector< TRouteFlashElement > RouteFlashVector
Definition: TrackUnit.h:1539
TInterface::SpeedButton80
TSpeedButton * SpeedButton80
Definition: InterfaceUnit.h:616
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5693
TInterface::ApproachLocking
void ApproachLocking(int Caller, TDateTime Now)
Function that deals with approach locking during ClockTimer2 function.
Definition: InterfaceUnit.cpp:20112
TInterface::TimetableTitle
AnsiString TimetableTitle
the titles of the loaded railway and loaded timetable, i.e. the filenames without the extension
Definition: InterfaceUnit.h:1262
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:487
TInterface::BlackBgndMenuItemClick
void __fastcall BlackBgndMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16314
TInterface::SpeedButton114
TSpeedButton * SpeedButton114
Definition: InterfaceUnit.h:650
TInterface::Deleting
@ Deleting
Definition: InterfaceUnit.h:1066
TInterface::RestoreAllDefaultLengthsButton
TBitBtn * RestoreAllDefaultLengthsButton
Definition: InterfaceUnit.h:115
TInterface::ClearAllMenuItemClick
void __fastcall ClearAllMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3354
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:113
TPrefDirElement::SetXLink
void SetXLink(int input)
Used in pasting pref dirs.
Definition: TrackUnit.h:352
TInterface::MoveTTEntryUpKeyFlag
bool MoveTTEntryUpKeyFlag
Definition: InterfaceUnit.h:1401
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:143
TInterface::TextBox
TEdit * TextBox
the edit box that accepts text to be added
Definition: InterfaceUnit.h:128
TGraphicElement::GetHPos
int GetHPos()
Definition: TrackUnit.h:450
TInterface::SpeedButton133
TSpeedButton * SpeedButton133
Definition: InterfaceUnit.h:669
TOnePrefDir::SearchVectorSize
unsigned int SearchVectorSize() const
Return the vector size.
Definition: TrackUnit.h:1426
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:759
Moderate
@ Moderate
Definition: Utilities.h:38
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:757
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10726
TInterface::SpeedButton18
TSpeedButton * SpeedButton18
Definition: InterfaceUnit.h:554
TInterface::ScreenGridFlag
bool ScreenGridFlag
true when the screen grid is displayed
Definition: InterfaceUnit.h:1342
TInterface::InfoCaptionStore
AnsiString InfoCaptionStore
temporary store for the information panel caption
Definition: InterfaceUnit.h:1256
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:897
TInterface::CopiedEntryFlag
bool CopiedEntryFlag
true when CopiedEntryStr holds a timetable entry in the timetable editor
Definition: InterfaceUnit.h:1286
TOneRoute::ConvertAndAddPreferredRouteSearchVector
void ConvertAndAddPreferredRouteSearchVector(int Caller, IDInt ReqPosRouteID, bool AutoSigsFlag)
Called after a preferred (i.e. preferred direction or automatic signals) route has been selected and ...
Definition: TrackUnit.cpp:16791
TInterface::SpeedButton89
TSpeedButton * SpeedButton89
Definition: InterfaceUnit.h:625
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:885
TInterface::BuildDatagramFromHostMap
void BuildDatagramFromHostMap(int Caller, TBytes &buffer, TDynamicMap DynamicMap)
converts a host's dynamic map into a datagram; TTTime is a 4 byte timetable time in secs (allows up t...
Definition: InterfaceUnit.cpp:28667
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:367
TPrefDirElement::SetEXNumber
void SetEXNumber(int input)
Used in pasting pref dirs.
Definition: TrackUnit.h:364
TInterface::AnyTTKeyFlagSet
bool AnyTTKeyFlagSet()
Definition: InterfaceUnit.h:1542
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:18139
TInterface::SetTopIndex
void SetTopIndex(int Caller)
This used in timetable functions when shift keys pressed to make sure that the highlighted entry rema...
Definition: InterfaceUnit.cpp:17895
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:773
TUtilities::ShowLongServRefsFlag
bool ShowLongServRefsFlag
when set long service references show on screen, initialised in Interface constructor
Definition: Utilities.h:83
TOnePrefDir::RealignAfterTrackErase
void RealignAfterTrackErase(int Caller, int ErasedTrackVectorPosition)
After a track element is erased the preferred direction elements are likely to be affected....
Definition: TrackUnit.cpp:14729
TInterface::PowerToggleButtonClick
void __fastcall PowerToggleButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16502
TInterface::TTimetableEditVector
std::vector< AnsiString > TTimetableEditVector
typedef for the complete timetable as a list of AnsiStrings (each a complete ttb line delimited by nu...
Definition: InterfaceUnit.h:1222
PrefDirCall
@ PrefDirCall
Definition: TrackUnit.h:1331
TOnePrefDir::BiDirectionalPrefDir
bool BiDirectionalPrefDir(int Caller, TPrefDir4MultiMapIterator PDPtr)
Determines whether the preferred direction pointed to has another pref dir in the opposite direction ...
Definition: TrackUnit.cpp:14504
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Major
@ Major
Definition: Utilities.h:38
TInterface::NoFailuresMenuItem
TMenuItem * NoFailuresMenuItem
Definition: InterfaceUnit.h:751
TInterface::SpeedButton46
TSpeedButton * SpeedButton46
Definition: InterfaceUnit.h:582
Points
@ Points
Definition: TrackUnit.h:66
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:12517
TInterface::PlayerMakingInitialContactFlag
bool PlayerMakingInitialContactFlag
Definition: InterfaceUnit.h:1202
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:879
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:16381
TTrain::RemoveLongServRef
void RemoveLongServRef(int Caller, AnsiString NameText, TDisplay *Disp)
Removes the displayed train service ref.
Definition: TrainUnit.cpp:2742
TInterface::Delay
void Delay(int Caller, double Msec)
Delays operation for the set time in milliseconds.
Definition: InterfaceUnit.cpp:18342
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:405
TTrainController::OpActionPanelHintDelayCounter
unsigned int OpActionPanelHintDelayCounter
new v2.2.0 on start operation delays the op action panel headcode display for about 3 secs while hint...
Definition: TrainUnit.h:918
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:860
TInterface::OutputLog7
TLabel * OutputLog7
Definition: InterfaceUnit.h:346
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:902
TInterface::LastNonCtrlOrShiftKeyDown
int LastNonCtrlOrShiftKeyDown
value of last key (other than Ctrl or Shift) pressed down - to prevent repeated FormKeyDown calls fro...
Definition: InterfaceUnit.h:1440
TTrack::WriteOperatingTrackAndTextToImage
void WriteOperatingTrackAndTextToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click to add all track & text to the image file in their ope...
Definition: TrackUnit.cpp:4255
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:57
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3648
TInterface::TextItem
int TextItem
used to store a single item of text
Definition: InterfaceUnit.h:1483
TInterface::UserGraphicButtonClick
void __fastcall UserGraphicButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17359
TInterface::OriginalTimetableEditVector
TTimetableEditVector OriginalTimetableEditVector
the complete timetable as a list of AnsiStrings for use in edit timetable functions
Definition: InterfaceUnit.h:1532
TGraphicElement::LoadOverlayGraphic
void LoadOverlayGraphic(int Caller, Graphics::TBitmap *Overlay)
Load the temporary overlay graphic.
Definition: TrackUnit.cpp:1900
TInterface::MPCPHostImage
TImage * MPCPHostImage
Definition: InterfaceUnit.h:713
TInterface::PresetAutoSigRoutesButtonClick
void __fastcall PresetAutoSigRoutesButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17108
TInterface::SigPrefNonConsecButtonClick
void __fastcall SigPrefNonConsecButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2385
TRailGraphics::SpeedBut68GrndBlackGlyph
Graphics::TBitmap * SpeedBut68GrndBlackGlyph
Definition: GraphicUnit.h:1093
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:417
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:533
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:18333
TInterface::MMovePrefDirSelFlag
bool MMovePrefDirSelFlag
Definition: InterfaceUnit.h:1315
TInterface::TwoLocationNamePanel
TPanel * TwoLocationNamePanel
Definition: InterfaceUnit.h:687
TInterface::InvalidIPAddress
bool InvalidIPAddress(AnsiString Text)
< Utility used in PopulateCouplingMap for ID conversion
Definition: InterfaceUnit.cpp:28847
Continuation
@ Continuation
Definition: TrackUnit.h:66
TInterface::ScreenUpButton
TBitBtn * ScreenUpButton
Definition: InterfaceUnit.h:291
TInterface::TTServiceSyntaxCheckButton
TButton * TTServiceSyntaxCheckButton
Definition: InterfaceUnit.h:183
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:846
TInterface::SetLengthsButton
TBitBtn * SetLengthsButton
Definition: InterfaceUnit.h:109
TUserGraphicItem::Height
int Height
Definition: DisplayUnit.h:36
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:487
GraphicUnit.h
TInterface::AppActivate
void __fastcall AppActivate(TObject *Sender)
Definition: InterfaceUnit.cpp:828
PerfLogUnit.h
TInterface::MPHPCancelButtonClick
void __fastcall MPHPCancelButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27325
TTrack::RebuildTrackAndText
void RebuildTrackAndText(int Caller, TDisplay *Disp, bool BothPointFilletsAndBasicLCs)
Called by TInterface::ClearandRebuildRailway to replot all the active and inactive track elements and...
Definition: TrackUnit.cpp:3801
TInterface::TimeOrderButton
TButton * TimeOrderButton
Definition: InterfaceUnit.h:760
TextUnit.h
TTrack::PlotPoints
void PlotPoints(int Caller, TTrackElement TrackElement, TDisplay *Disp, bool BothFillets)
Plot points on screen according to how they are set (Attribute value), or, with both fillets if BothF...
Definition: TrackUnit.cpp:6183
TInterface::AddTextButtonClick
void __fastcall AddTextButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1190
TInterface::MMoveTrackSelFlag
bool MMoveTrackSelFlag
Definition: InterfaceUnit.h:1314
TDisplay::DisplayZoomOutOffsetV
static int DisplayZoomOutOffsetV
the verticalal offset of the zoomed-out display
Definition: DisplayUnit.h:87
TInterface::ReselectMenuItemClick
void __fastcall ReselectMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11759
TInterface::SpeedButton94
TSpeedButton * SpeedButton94
Definition: InterfaceUnit.h:630
TUtilities::RepairDiagnosisTime
float RepairDiagnosisTime
Definition: Utilities.h:70
TInterface::SaveTTAsKeyFlag
bool SaveTTAsKeyFlag
Definition: InterfaceUnit.h:1414
TInterface::SaveTTEntryButtonClick
void __fastcall SaveTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4673
TInterface::TimetablePanelLabel
TLabel * TimetablePanelLabel
label to the left of TimetablePanel
Definition: InterfaceUnit.h:359
TTrack::PointFlashFlag
bool PointFlashFlag
< true if a route set through an LC that is closed to trains (& therefore needs to be opened)
Definition: TrackUnit.h:771
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:895
TInterface::RightClickTrainMousePosY
int RightClickTrainMousePosY
used to retain the mouse position on the train for SkipTimetabledActionsMenuItemClick
Definition: InterfaceUnit.h:1467
TInterface::FirstPerfLogFormDisplay
bool FirstPerfLogFormDisplay
places PerfLogForm at bottom LHS when first displayed
Definition: InterfaceUnit.h:1302
TActionsDueForm::ActionsDuePanel
TPanel * ActionsDuePanel
Definition: ActionsDueUnit.h:16
TInterface::LoadConfigFile
void LoadConfigFile(int Caller, bool FirstLoad, bool &NoConfigFile)
Load the configuration file, only allow default track element length and speed limit to be loaded it ...
Definition: InterfaceUnit.cpp:17573
TRailGraphics::CouplingExit9
Graphics::TBitmap * CouplingExit9
Definition: GraphicUnit.h:563
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:505
TInterface::SpeedButton40
TSpeedButton * SpeedButton40
Definition: InterfaceUnit.h:576
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:11548
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TDisplay::Left
int Left()
Return the left pixel position of the screen.
Definition: DisplayUnit.h:115
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:377
TInterface::BufferAttentionImage
TImage * BufferAttentionImage
Definition: InterfaceUnit.h:302
TOnePrefDir::ValidatePrefDir
bool ValidatePrefDir(int Caller)
Checks that all elements in PrefDirVector have been properly set, i.e. don't have their default value...
Definition: TrackUnit.cpp:13258
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:21959
TInterface::OperatorActionButtonClick
void __fastcall OperatorActionButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2530
TInterface::SpeedEditBox
TEdit * SpeedEditBox
Definition: InterfaceUnit.h:219
TInterface::InfoVector
TInfoVector InfoVector
Definition: InterfaceUnit.h:1114
TInterface::NumPlayers
int NumPlayers
Definition: InterfaceUnit.h:1190
TInterface::LoadRailwayDialog
TOpenDialog * LoadRailwayDialog
Definition: InterfaceUnit.h:520
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:716
TInterface::CopyMenuItem
TMenuItem * CopyMenuItem
Definition: InterfaceUnit.h:467
TInterface::ResetDefaultLengthButtonClick
void __fastcall ResetDefaultLengthButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1759
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:57
TInterface::NoOperMode
@ NoOperMode
Definition: InterfaceUnit.h:1055
TInterface::ErrorLogCalledFlag
bool ErrorLogCalledFlag
true when an error has been thrown, stops repeated calls to ErrorLog and stops the MasterClockTimer f...
Definition: InterfaceUnit.h:1296
TInterface::NextTTEntryKeyFlag
bool NextTTEntryKeyFlag
Definition: InterfaceUnit.h:1400
TInterface::RouteFlashStartTime
TDateTime RouteFlashStartTime
stores the starting time (timetable clock time) for route flashing
Definition: InterfaceUnit.h:1499
TInterface::BlueBgndMenuItemClick
void __fastcall BlueBgndMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16366
TInterface::P5SCounter
int P5SCounter
Definition: InterfaceUnit.h:1193
TInterface::SetTrackLengths
void SetTrackLengths(int Caller, int Distance, int SpeedLimit)
Called during track building when setting distances, to calculate and set the individual track elemen...
Definition: InterfaceUnit.cpp:25799
TInterface::SpeedToggleButtonClick
void __fastcall SpeedToggleButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16392
TTrack::FailedPointsVector
TFailedElementVector FailedPointsVector
Definition: TrackUnit.h:795
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:11107
TTrack::CheckTrackElementsInFile
bool CheckTrackElementsInFile(int Caller, int &NumberOfActiveElements, bool &GraphicsFollow, std::ifstream &VecFile)
True if TrackElements in the file are all valid.
Definition: TrackUnit.cpp:3418
TInterface::ModerateDelaysMenuItem
TMenuItem * ModerateDelaysMenuItem
Definition: InterfaceUnit.h:748
TGraphicElement::GetVPos
int GetVPos()
Definition: TrackUnit.h:455
TOneRoute::StartSelectionRouteID
IDInt StartSelectionRouteID
needed for session saves as routes in build are not saved in sessions
Definition: TrackUnit.h:1559
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:88
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:339
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:511
TInterface::TTLabel5
TLabel * TTLabel5
Definition: InterfaceUnit.h:386
TInterface::HomeButtonClick
void __fastcall HomeButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11639
TInterface::PointFlashStartTime
TDateTime PointFlashStartTime
stores the starting time (timetable clock time) for points flashing
Definition: InterfaceUnit.h:1497
TInterface::TTLabel7
TLabel * TTLabel7
Definition: InterfaceUnit.h:388
TInterface::RailwayWebSiteMenuItemClick
void __fastcall RailwayWebSiteMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16306
TInterface::BuildDatagramFromPlayerMap
void BuildDatagramFromPlayerMap(int Caller, char marker, AnsiString UserName, TBytes &buffer, TDynamicMap DynamicMap)
converts a player's dynamic map into a datagram; marker is a single digit to identify the datagram ty...
Definition: InterfaceUnit.cpp:28511
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:717
TInterface::UpdateActionsDuePanel
void UpdateActionsDuePanel(int Caller)
Called every 5 secs to update the panel (if visible)
Definition: InterfaceUnit.cpp:26339
Minor
@ Minor
Definition: Utilities.h:38
TInterface::SpeedButton36
TSpeedButton * SpeedButton36
Definition: InterfaceUnit.h:572
TInterface::ClockTimer2
void ClockTimer2(int Caller)
The main loop, called every clock tick via MasterClockTimer.
Definition: InterfaceUnit.cpp:10252
TInterface::SpeedButton56
TSpeedButton * SpeedButton56
Definition: InterfaceUnit.h:592
TDisplay::PlotDashedRect
void PlotDashedRect(int Caller, TRect Rect)
Plot a dashed rectangle for the area defined by Rect, used when selecting display areas.
Definition: DisplayUnit.cpp:501
TInterface::HideReminderListBox
void HideReminderListBox(int Caller)
makes ReminderListBox invisible (if it was visible)
Definition: InterfaceUnit.cpp:14836
TInterface::NewTTEntryButtonClick
void __fastcall NewTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3843
TInterface::LoadSessionMenuItemClick
void __fastcall LoadSessionMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3340
TInterface::SkipTTActionsListBox
TListBox * SkipTTActionsListBox
Definition: InterfaceUnit.h:739
TTrack::SelectVectorClear
void SelectVectorClear()
Definition: TrackUnit.h:952
TInterface::ShowPerfLogForm
bool ShowPerfLogForm
true when the 'show performance panel' button has been clicked during operation
Definition: InterfaceUnit.h:1025
TInterface::PerformanceFileName
AnsiString PerformanceFileName
full path and filename of the performance file
Definition: InterfaceUnit.h:1260
TInterface::AutoSigsButton
TBitBtn * AutoSigsButton
Definition: InterfaceUnit.h:235
TInterface::PopupMenu
TPopupMenu * PopupMenu
Definition: InterfaceUnit.h:517
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:897
TInterface::LoadSessionFlag
bool LoadSessionFlag
true when a session load command has been given - implemented at next clock tick
Definition: InterfaceUnit.h:1310
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:269
TInterface::NoTrackMode
@ NoTrackMode
Definition: InterfaceUnit.h:1065
TInterface::TimetableValidFlag
bool TimetableValidFlag
indicates that a 'Validate timetable' button click in the timetable editor has succeeded
Definition: InterfaceUnit.h:1378
TInterface::ElapsedTimeTestFunctionStart
bool ElapsedTimeTestFunctionStart
Definition: InterfaceUnit.h:1238
TInterface::ADFLeft
int ADFLeft
stores the ADForm position for re-use when made visible.
Definition: InterfaceUnit.h:1038
TInterface::CancelSelectionFlag
bool CancelSelectionFlag
used in case pasting to avoid RecoverClipboard call when set
Definition: InterfaceUnit.h:1280
TInterface::TimetableControlMenuItem
TMenuItem * TimetableControlMenuItem
Definition: InterfaceUnit.h:493
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:355
TUtilities::PerformanceFile
std::ofstream PerformanceFile
the file where the performance log for a particular period of operation is saved
Definition: Utilities.h:109
TInterface::ConvertToOtherHandSignalsMenuItemClick
void __fastcall ConvertToOtherHandSignalsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17219
TInterface::TTLabel15
TLabel * TTLabel15
Definition: InterfaceUnit.h:395
TInterface::PreferredRoute
bool PreferredRoute
true when AutoSig or preferred route building selected during operation (always same state as ConsecS...
Definition: InterfaceUnit.h:1328
TInterface::ConvertCRLFsToCommas
void ConvertCRLFsToCommas(int Caller, AnsiString &ConvStr)
Used in timetable editing functions to convert any CRLFs in intended service entries to commas.
Definition: InterfaceUnit.cpp:6957
TInterface::OutputLog2MouseDown
void __fastcall OutputLog2MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16182
TInterface::MPHPGeneralLabel
TLabel * MPHPGeneralLabel
Definition: InterfaceUnit.h:721
TDisplay::DisplayZoomOutOffsetH
static int DisplayZoomOutOffsetH
the horizontal offset of the zoomed-out display
Definition: DisplayUnit.h:85
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:554
TInterface::SpeedButton106
TSpeedButton * SpeedButton106
Definition: InterfaceUnit.h:642
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:54
TInterface::LoadTimetableFromSessionFile
bool LoadTimetableFromSessionFile(int Caller, std::ifstream &SessionFile)
Loads timetable into memory from a session file, true if successful.
Definition: InterfaceUnit.cpp:24090
TTrack::RotRightArray
int RotRightArray[FirstUnusedSpeedTagNumber]
holds TrackElement SpeedTag values for 'rotating right' via menu items 'Edit' & 'Rotate right'
Definition: TrackUnit.h:791
TInterface::SpeedButton125
TSpeedButton * SpeedButton125
Definition: InterfaceUnit.h:661
TInterface::SpeedButton21
TSpeedButton * SpeedButton21
Definition: InterfaceUnit.h:557
TUtilities::DateTimeStamp
AnsiString DateTimeStamp()
creates a string of the form 'dd/mm/yyyy hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:67
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:905
TTrack::ResetPoints
void ResetPoints(int Caller)
Called on exit from operation to reset all points to non-diverging or to left fork (Attribute = 0),...
Definition: TrackUnit.cpp:4804
TInterface::SpeedButton64
TSpeedButton * SpeedButton64
Definition: InterfaceUnit.h:600
TInterface::AllEntriesTTListBoxMouseUp
void __fastcall AllEntriesTTListBoxMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:6418
TInterface::TwoLocationNameCheckBox
TCheckBox * TwoLocationNameCheckBox
Definition: InterfaceUnit.h:690
TAllRoutes::AllRoutesSize
unsigned int AllRoutesSize() const
Returns the number of routes in the railway.
Definition: TrackUnit.h:1756
TTrack::DuplicatedLocationName
bool DuplicatedLocationName(int Caller, bool GiveMessage)
examines LocationNameMultiMap and returns true if there are two or more locations with the same name ...
Definition: TrackUnit.cpp:8936
TInterface::FontButton
TBitBtn * FontButton
Definition: InterfaceUnit.h:107
TDisplay::Ellipse
void Ellipse(int Caller, int HPos, int VPos, TColor Col)
Plot an ellipse at the defined position and with the defined colour.
Definition: DisplayUnit.cpp:125
TTrack::LCVector
TLCVector LCVector
vector of level crossing InactiveTrackVector positions
Definition: TrackUnit.h:813
TInterface::NextTTEntryButtonClick
void __fastcall NextTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3761
TInterface::TrainLeaveWarningSent
bool TrainLeaveWarningSent
indicates that a train under signaller control leaving a location when awaiting departure warning tha...
Definition: InterfaceUnit.h:1360
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:233
TInterface::~TInterface
__fastcall ~TInterface()
destructor
Definition: InterfaceUnit.cpp:727
TInterface::CancelTTEntryButtonClick
void __fastcall CancelTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5247
TInterface::TTTimeSaveWarningNotRequired
bool TTTimeSaveWarningNotRequired
if false a warning is given when attempting to save a timetable in time order, if the save proceeds t...
Definition: InterfaceUnit.h:1364
TUtilities::NilSignalChangeEventsPerFailure
int NilSignalChangeEventsPerFailure
Definition: Utilities.h:58
TInterface::PlayerCancelJoinFlag
bool PlayerCancelJoinFlag
Definition: InterfaceUnit.h:1204
TInterface::LengthHeatMapImageRedHigh
TImage * LengthHeatMapImageRedHigh
Definition: InterfaceUnit.h:314
TInterface::SaveHeaderMenu1Click
void __fastcall SaveHeaderMenu1Click(TObject *Sender)
Definition: InterfaceUnit.cpp:3277
TInterface::TRlyUserInfo::TRlyUserInfo
TRlyUserInfo()
Definition: InterfaceUnit.cpp:28918
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:710
TInterface::TTClockAdjPanel
TPanel * TTClockAdjPanel
Definition: InterfaceUnit.h:252
TInterface::BuildTrainDataVectorForLoadFile
bool BuildTrainDataVectorForLoadFile(int Caller, std::ifstream &TTBLFile, bool GiveMessages, bool CheckLocationsExistInRailway, bool SessionFile)
Convert a stored timetable file (either as a stand alone file or within a session file) to a loaded t...
Definition: InterfaceUnit.cpp:24313
TInterface::ConflictPanel
TPanel * ConflictPanel
Definition: InterfaceUnit.h:276
TInterface::CopiedEntryStr
AnsiString CopiedEntryStr
a timetable entry that has been copied
Definition: InterfaceUnit.h:1246
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:717
TInterface::SpeedButton12
TSpeedButton * SpeedButton12
Definition: InterfaceUnit.h:548
TAllRoutes::SaveRoutes
void SaveRoutes(int Caller, std::ofstream &OutFile)
Save railway route information to a session file or an error file.
Definition: TrackUnit.cpp:21357
TInterface::SelectMenuItem
TMenuItem * SelectMenuItem
Definition: InterfaceUnit.h:464
TInterface::BuildDynamicMapFromPlayerDatagram
bool BuildDynamicMapFromPlayerDatagram(int Caller, TDynamicMap &DMap, TBytes Buffer, unsigned char &marker, AnsiString &UserName)
Converse of BuildDatagramFromPlayerMap.
Definition: InterfaceUnit.cpp:28575
TInterface::MoveTTEntryDownKeyFlag
bool MoveTTEntryDownKeyFlag
Definition: InterfaceUnit.h:1402
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:69
TGraphicElement::PlotOverlay
void PlotOverlay(int Caller, TDisplay *Disp)
Plot the overlay graphic on screen.
Definition: TrackUnit.cpp:1910
TTrack::ResetSignals
void ResetSignals(int Caller)
Called on exit from operation to reset all signals to red (Attribute = 0), failed to false & clear Fa...
Definition: TrackUnit.cpp:4786
Connection
@ Connection
Definition: TrackUnit.h:76
TInterface::TTTextButtonClick
void __fastcall TTTextButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5406
TInterface::TDynamicMap
std::map< TNumHVPair, TServiceInfo, TDynamicMapComp > TDynamicMap
Definition: InterfaceUnit.h:1142
TTrack::LoadBarriersDownVector
void LoadBarriersDownVector(int Caller, std::ifstream &VecFile)
Load all BarriersDownVector values from SessionFile.
Definition: TrackUnit.cpp:3778
TInterface::InvertTTEntryButton
TButton * InvertTTEntryButton
Definition: InterfaceUnit.h:756
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:846
TAllRoutes::RouteTruncateFlag
bool RouteTruncateFlag
Definition: TrackUnit.h:1731
TInterface::LengthOKButton
TBitBtn * LengthOKButton
distance/speed setting buttons - left to right & top to bottom
Definition: InterfaceUnit.h:118
TInterface::IsBecomeNewServiceAvailable
bool IsBecomeNewServiceAvailable(int Caller, int TrainID, AnsiString &NextServiceRef)
Check to see if a BecomeNewService popup otion is available - i.e stopped at location,...
Definition: InterfaceUnit.cpp:15000
TTrain::NextTrainID
static int NextTrainID
< km/h
Definition: TrainUnit.h:334
TInterface::PreviousTTEntryButtonClick
void __fastcall PreviousTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3802
TInterface::UserGraphicButton
TBitBtn * UserGraphicButton
Definition: InterfaceUnit.h:120
TOneRoute::ConvertAndAddNonPreferredRouteSearchVector
void ConvertAndAddNonPreferredRouteSearchVector(int Caller, IDInt ReqPosRouteID)
Called after a non-preferred (i.e. unrestricted) route has been selected and has finished flashing,...
Definition: TrackUnit.cpp:18146
TTrack::PopulateSimpleVector
void PopulateSimpleVector(int Caller)
clear then add all simple element track vector positions to the vector, added at v2....
Definition: TrackUnit.cpp:12487
TInterface::Text_Y
int Text_Y
as above for 'Y'
Definition: InterfaceUnit.h:1479
TInterface::MPCPHostIPEditBox
TEdit * MPCPHostIPEditBox
Definition: InterfaceUnit.h:724
TTrack::RepairTSR
void RepairTSR(TFailedElementVector::iterator FPVIt)
remove TSR, added at v2.13.0
Definition: TrackUnit.cpp:12459
TInterface::MMoveCopyCutSelPickedUpFlag
bool MMoveCopyCutSelPickedUpFlag
Definition: InterfaceUnit.h:1316
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:20616
TInterface::MultiplayerHostPanel
TPanel * MultiplayerHostPanel
Definition: InterfaceUnit.h:716
TInterface::SigAutoNonConsecButtonClick
void __fastcall SigAutoNonConsecButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2303
TInterface::DeleteTTEntryButton
TButton * DeleteTTEntryButton
Definition: InterfaceUnit.h:173
TInterface::ReselectUserGraphicClick
void __fastcall ReselectUserGraphicClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17391
TActionVectorEntry::MinDwellTime
double MinDwellTime
Definition: TrainUnit.h:133
TRailGraphics::SolidCircleRed
Graphics::TBitmap * SolidCircleRed
Definition: GraphicUnit.h:565
TInterface::TTClockAdd1mButtonClick
void __fastcall TTClockAdd1mButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17031
TInterface::SubMinsButton
TButton * SubMinsButton
Definition: InterfaceUnit.h:182
TInterface::SpeedButton66
TSpeedButton * SpeedButton66
Definition: InterfaceUnit.h:602
TTrack::PlotPlainRaisedLinkedLevelCrossingBarriersAndSetMarkers
void PlotPlainRaisedLinkedLevelCrossingBarriersAndSetMarkers(int Caller, int BaseElementSpeedTag, int HLoc, int VLoc, TDisplay *Disp)
Plot LC elements without any base elements, and set LCPlotted true - used in ClearandRebuildRailway.
Definition: TrackUnit.cpp:7339
TPrefDirElement::GetPrefDirGraphicPtr
Graphics::TBitmap * GetPrefDirGraphicPtr()
picks up the EXGraphicPtr for preferred directions
Definition: TrackUnit.cpp:567
TInterface::ValidateTimetableButtonClick
void __fastcall ValidateTimetableButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5064
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:461
TInterface::UserGraphicMoveVPos
int UserGraphicMoveVPos
used to store the original user graphic 'H' & 'V' positions for use during moving
Definition: InterfaceUnit.h:1489
TInterface::SpeedButton98
TSpeedButton * SpeedButton98
Definition: InterfaceUnit.h:634
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:125
TInterface::LoadTimetableMenuItem
TMenuItem * LoadTimetableMenuItem
Definition: InterfaceUnit.h:447
TInterface::SpeedButton95
TSpeedButton * SpeedButton95
Definition: InterfaceUnit.h:631
TPrefDirElement::SetTrackVectorPosition
void SetTrackVectorPosition(int TVPos)
Used in pasting pref dirs.
Definition: TrackUnit.h:329
TInterface::LocationNameButtonClick
void __fastcall LocationNameButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1275
TTrack::InactiveTrackElementPresentAtHV
bool InactiveTrackElementPresentAtHV(int Caller, int HLoc, int VLoc)
New at v1.2.0; true if an inactive track element present.
Definition: TrackUnit.cpp:5971
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:155
TInterface::SaveMenuItem
TMenuItem * SaveMenuItem
Definition: InterfaceUnit.h:446
TInterface::GapSetting
@ GapSetting
Definition: InterfaceUnit.h:1065
TTrackElement::StationEntryStopLinkPos4
int StationEntryStopLinkPos4
Used for track at platforms ( 1 & 2) and non-station named locations (1 - 4) to mark the train front ...
Definition: TrackUnit.h:153
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:347
TInterface::ShowHideTTButton
TBitBtn * ShowHideTTButton
Definition: InterfaceUnit.h:164
TInterface::ScreenDownButton
TBitBtn * ScreenDownButton
Definition: InterfaceUnit.h:292
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:381
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:889
TInterface::ShowHideStringGridMenuItem
TMenuItem * ShowHideStringGridMenuItem
Definition: InterfaceUnit.h:738
TInterface::DeleteOnePrefDirButton
TBitBtn * DeleteOnePrefDirButton
Definition: InterfaceUnit.h:157
TInterface::PauseEntryTTClockSpeed
float PauseEntryTTClockSpeed
rate at which the timetable clock runs on entry to the adjust routine - to restore if cancelled
Definition: InterfaceUnit.h:1421
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10693
TInterface::MetreVariableLabel
TLabel * MetreVariableLabel
Definition: InterfaceUnit.h:143
TInterface::AllEntriesTTListBoxTopPosition
int AllEntriesTTListBoxTopPosition
stores the TopIndex property when keys are used to select items in the TT edit panel
Definition: InterfaceUnit.h:1438
TInterface::SigImagePanel
TPanel * SigImagePanel
new at v2.3.0 for handed signals
Definition: InterfaceUnit.h:427
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:8252
TTrack::CopyFlag
bool CopyFlag
true only when copying a selection, used to prevent location names being copied
Definition: TrackUnit.h:753
TInterface::CPGenFileButtonClick
void __fastcall CPGenFileButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17512
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:19845
TInterface::TTClockAdd10mButtonClick
void __fastcall TTClockAdd10mButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17011
TRailGraphics::CouplingExit7
Graphics::TBitmap * CouplingExit7
Definition: GraphicUnit.h:561
TRailGraphics::SpeedBut73GrndBlackGlyph
Graphics::TBitmap * SpeedBut73GrndBlackGlyph
Definition: GraphicUnit.h:1098
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:7097
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:433
TTrack::CheckMapAndTrack
void CheckMapAndTrack(int Caller)
Validity test.
Definition: TrackUnit.cpp:7938
TTrack::LoadGraphics
void LoadGraphics(int Caller, std::ifstream &VecFile, UnicodeString GraphicsPath)
new at v2.4.0, load user graphics
Definition: TrackUnit.cpp:3180
TInterface::SkipTimetabledActionsMenuItem
TMenuItem * SkipTimetabledActionsMenuItem
Definition: InterfaceUnit.h:498
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10711
TInterface::SpeedButton30
TSpeedButton * SpeedButton30
Definition: InterfaceUnit.h:566
TTrackElement::LocationName
AnsiString LocationName
location name not used for timetabling, only for identification: platforms, non-station named locatio...
Definition: TrackUnit.h:132
TInterface::TTClockSpeedLabel
TLabel * TTClockSpeedLabel
Definition: InterfaceUnit.h:375
TTrack::FourAspectBuild
@ FourAspectBuild
Definition: TrackUnit.h:876
TInterface::SetReminderMenuItem
TMenuItem * SetReminderMenuItem
Definition: InterfaceUnit.h:761
TInterface::ManualLCDownImage
TImage * ManualLCDownImage
Definition: InterfaceUnit.h:308
TInterface::SpeedButton146
TSpeedButton * SpeedButton146
Definition: InterfaceUnit.h:682
TTrainController::OpTimeToActMultiMapIterator
TOpTimeToActMultiMapIterator OpTimeToActMultiMapIterator
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:928
TTrack::TActiveLevelCrossing::HLoc
int HLoc
HLoc value for found level crossing element.
Definition: TrackUnit.h:631
TInterface::NewEntryInPreparationFlag
bool NewEntryInPreparationFlag
true when a new timetable entry is being prepared in the timetable editor
Definition: InterfaceUnit.h:1320
TInterface::ConsecutiveSelfUpdates
int ConsecutiveSelfUpdates
counts time without an internet connection, cancels session for player if no contact for at least 5 m...
Definition: InterfaceUnit.h:1157
TInterface::EIdExceptionSource
AnsiString EIdExceptionSource
Definition: InterfaceUnit.h:1194
TDisplay::DisplayOffsetH
static int DisplayOffsetH
the horizontal offset of the displayed screen (as viewpoint moves to the right [railway moves left] t...
Definition: DisplayUnit.h:77
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:151
TActionsDueForm::ActionsDueListBox
TListBox * ActionsDueListBox
Definition: ActionsDueUnit.h:18
TInterface::GetVersion
UnicodeString GetVersion()
determined automatically from the project options 'Version Info'
Definition: InterfaceUnit.cpp:847
TInterface::OneEntryTimetableContents
AnsiString OneEntryTimetableContents
the current text in the large right hand timetable edit window
Definition: InterfaceUnit.h:1258
TInterface::SelectRect
TRect SelectRect
the rectangle in HLoc & VLoc terms set in Edit->Select & Edit->Reselect
Definition: InterfaceUnit.h:1523
TPrefDirElement::EntryExitNumber
bool EntryExitNumber()
determines and loads EXNumber (see above)
Definition: TrackUnit.cpp:319
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:356
TInterface::LocationNameComboBox
TComboBox * LocationNameComboBox
the combobox that lists location names
Definition: InterfaceUnit.h:213
TInterface::ValidateTimetableKeyFlag
bool ValidateTimetableKeyFlag
Definition: InterfaceUnit.h:1412
TTrack::SkipLocationNameMultiMapCheck
bool SkipLocationNameMultiMapCheck
changed from PastingWithAttributes in v2.4.0 as all pastes are now with attributes - needed to suppre...
Definition: TrackUnit.h:775
TInterface::ExitOperationButton
TBitBtn * ExitOperationButton
Definition: InterfaceUnit.h:243
TInterface::TTLabel6
TLabel * TTLabel6
Definition: InterfaceUnit.h:387
TInterface::SpeedButton97
TSpeedButton * SpeedButton97
Definition: InterfaceUnit.h:633
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:17932
TInterface::JoinMultiplayerSessionMenuItemClick
void __fastcall JoinMultiplayerSessionMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:28204
TInterface::SignalStopImage
TImage * SignalStopImage
Definition: InterfaceUnit.h:301
TInterface::SpeedButton13
TSpeedButton * SpeedButton13
Definition: InterfaceUnit.h:549
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:71
TInterface::OutputLog9
TLabel * OutputLog9
Definition: InterfaceUnit.h:348
TTrack::RepairFailedSignals
void RepairFailedSignals(TFailedElementVector::iterator FPVIt)
restore signal to unfailed state, added at v2.13.0
Definition: TrackUnit.cpp:12397
TInterface::SetCaption
void SetCaption(int Caller)
Sets the railway and timetable titles at the top of the screen.
Definition: InterfaceUnit.cpp:22060
TInterface::SpeedButton27
TSpeedButton * SpeedButton27
Definition: InterfaceUnit.h:563
TOnePrefDir::ExternalClearPrefDirAnd4MultiMap
void ExternalClearPrefDirAnd4MultiMap()
Empty the existing preferred direction vector & map - for use by other classes.
Definition: TrackUnit.h:1432
TInterface::SetInitialPrefDirModeEditMenu
void SetInitialPrefDirModeEditMenu()
Enables or disables the initial Edit mode submenu items in PrefDir mode.
Definition: InterfaceUnit.cpp:26174
TInterface::MainScreenMouseMove
void __fastcall MainScreenMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:9456
TInterface::DistanceContinuing
@ DistanceContinuing
Definition: InterfaceUnit.h:1065
TInterface::SpeedsHeatmapButtonClick
void __fastcall SpeedsHeatmapButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:29529
TTrack::TUserGraphicMapEntry
std::pair< AnsiString, TPicture * > TUserGraphicMapEntry
an entry for TUserGraphicMap
Definition: TrackUnit.h:657
TInterface::PlayerOneSecondTimer
int PlayerOneSecondTimer
Definition: InterfaceUnit.h:1192
TInterface::CutTTEntryKeyFlag
bool CutTTEntryKeyFlag
Definition: InterfaceUnit.h:1405
TPrefDirElement::SetELinkPos
void SetELinkPos(int input)
Used in pasting pref dirs.
Definition: TrackUnit.h:346
TTrainController::MinsToAnsiTime
AnsiString MinsToAnsiTime(int Input)
converts an integer minute value to string "HH:MM" added at v2.15.0
Definition: TrainUnit.cpp:17313
TInterface::OneRailwayCouplingPair
TCouplingPair OneRailwayCouplingPair
Definition: InterfaceUnit.h:1153
TInterface::SigsOnLeftImage2
TImage * SigsOnLeftImage2
Definition: InterfaceUnit.h:329
TInterface::ChainEdit
TEdit * ChainEdit
Definition: InterfaceUnit.h:139
TInterface::AddRefDigits
void AddRefDigits(AnsiString AnsiServRef, int Position, AnsiString &EntryCopy, int Digits)
Used in ExpandRepeatsButtonClick function to add digits to a service reference.
Definition: InterfaceUnit.cpp:6301
TInterface::SpeedButton109
TSpeedButton * SpeedButton109
Definition: InterfaceUnit.h:645
TInterface::TrainTTShowing
bool TrainTTShowing
flags for floating information
Definition: InterfaceUnit.h:1172
TInterface::CPAtLocCheckBox
TCheckBox * CPAtLocCheckBox
Definition: InterfaceUnit.h:284
TInterface::PasteTTEntryButtonClick
void __fastcall PasteTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4533
TInterface::SpeedButton100
TSpeedButton * SpeedButton100
Definition: InterfaceUnit.h:636
TInterface::TestFunction
void TestFunction()
Called for diagnostic purposes when keys CTRL ALT 4 pressed.
Definition: InterfaceUnit.cpp:26286
TInterface::ExportTTKeyFlag
bool ExportTTKeyFlag
Definition: InterfaceUnit.h:1416
TOnePrefDir::GetNextPrefDirElement
bool GetNextPrefDirElement(int Caller, int HLoc, int VLoc, bool &FinishElement)
Used when continuing a chain of preferred directions or element lengths. Tries to find a set of linke...
Definition: TrackUnit.cpp:12637
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:403
TInterface::MovingTrainPresentOnFlashingRoute
bool MovingTrainPresentOnFlashingRoute(int Caller)
Examines a flashing route (i.e. one being set) and returns true if a moving train is detected on it a...
Definition: InterfaceUnit.cpp:18378
TInterface::Level2TrackMode
enum TInterface::TLevel2TrackMode Level2TrackMode
TInterface::SpeedButton65
TSpeedButton * SpeedButton65
Definition: InterfaceUnit.h:601
TInterface::DeleteTTEntryKeyFlag
bool DeleteTTEntryKeyFlag
Definition: InterfaceUnit.h:1407
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:891
TInterface::SaveRailwayBaseModeButton
TBitBtn * SaveRailwayBaseModeButton
Save button at the top left hand corner of the screen when no mode is selected.
Definition: InterfaceUnit.h:97
TInterface::MPCPUserName
AnsiString MPCPUserName
Definition: InterfaceUnit.h:1195
TInterface::SelectBiDirPrefDirsMenuItem
TMenuItem * SelectBiDirPrefDirsMenuItem
Definition: InterfaceUnit.h:474
TInterface::UserGraphicReselectPanel
TPanel * UserGraphicReselectPanel
Definition: InterfaceUnit.h:515
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:100
TInterface::RotLeftMenuItem
TMenuItem * RotLeftMenuItem
Definition: InterfaceUnit.h:509
TInterface::TTClockxQuarterButtonClick
void __fastcall TTClockxQuarterButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16933
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:860
TInterface::TextOrUserGraphicGridVal
int TextOrUserGraphicGridVal
stores the text alignment grid value, cycles forwards through 1, 2, 4, 8 & 16 each time the text grid...
Definition: InterfaceUnit.h:1481
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:70
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:184
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:201
TInterface::FileMenu
TMenuItem * FileMenu
Definition: InterfaceUnit.h:443
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:912
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:883
TInterface::OutputLog3MouseDown
void __fastcall OutputLog3MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:16191
TInterface::SpeedConversionPanel
TPanel * SpeedConversionPanel
Definition: InterfaceUnit.h:146
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:858
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:524
TInterface::SpeedButton22
TSpeedButton * SpeedButton22
Definition: InterfaceUnit.h:558
TTrack::LengthHeatMapFlag
bool LengthHeatMapFlag
true when plotting a length heatmap
Definition: TrackUnit.h:763
TTextHandler::SaveText
void SaveText(int Caller, std::ofstream &VecFile)
save the railway's text to VecFile
Definition: TextUnit.cpp:338
TUtilities::PointChangeEventsPerFailure
int PointChangeEventsPerFailure
number of points changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:99
TInterface::TwoLocationNameLabel
TLabel * TwoLocationNameLabel
Definition: InterfaceUnit.h:688
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:513
TInterface::SpeedButton130
TSpeedButton * SpeedButton130
Definition: InterfaceUnit.h:666
TTrack::GetGapHLoc
int GetGapHLoc()
Definition: TrackUnit.h:880
TInterface::ResetDefaultLengthButton
TBitBtn * ResetDefaultLengthButton
Definition: InterfaceUnit.h:116
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TInterface::PauseEntryRestartTime
double PauseEntryRestartTime
time value of the timetable restart time (as a double) on entry to pause mode
Definition: InterfaceUnit.h:1418
TUtilities::MajorPointChangeEventsPerFailure
int MajorPointChangeEventsPerFailure
Definition: Utilities.h:66
TInterface::MajorDelaysMenuItem
TMenuItem * MajorDelaysMenuItem
Definition: InterfaceUnit.h:749
TInterface::SelectBiDirPrefDirsMenuItemClick
void __fastcall SelectBiDirPrefDirsMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12884
TRailGraphics::SetUpAllDerivitiveGraphics
void SetUpAllDerivitiveGraphics(TColor TransparentColour)
Definition: GraphicUnit.cpp:4566
TInterface::SigsOnRightImage2
TImage * SigsOnRightImage2
Definition: InterfaceUnit.h:331
TTrack::LocationNameMultiMap
TLocationNameMultiMap LocationNameMultiMap
multimap of location names (see type for more information above)
Definition: TrackUnit.h:819
TTrack::NoNamedLocationElements
bool NoNamedLocationElements(int Caller)
True if there are no NamedLocationElements (includes footcrossings)
Definition: TrackUnit.cpp:4677
TTrain::SelSkipString
UnicodeString SelSkipString
the selected timetable string when skipping timetabled events
Definition: TrainUnit.h:550
TInterface::MultiplayerHostSessionMenuItem
TMenuItem * MultiplayerHostSessionMenuItem
Definition: InterfaceUnit.h:714
TInterface::AddTrack
@ AddTrack
Definition: InterfaceUnit.h:1065
TTrack::PlotSmallFlashingLinkedLevelCrossings
void PlotSmallFlashingLinkedLevelCrossings(int Caller, int HLoc, int VLoc, Graphics::TBitmap *GraphicPtr, TDisplay *Disp)
Plots either a LC or a blank element to flash manual LCs in zoomout mode.
Definition: TrackUnit.cpp:7801
TTrack::EraseTrackElement
void EraseTrackElement(int Caller, int HLocInput, int VLocInput, int &ErasedTrackVectorPosition, bool &TrackEraseSuccessfulFlag, bool InternalChecks)
Erases all active and inactive track elements at HLocInput & VLocInput from the vectors,...
Definition: TrackUnit.cpp:2005
TInterface::ClearAllMenuItem
TMenuItem * ClearAllMenuItem
Definition: InterfaceUnit.h:450
API
a class which handles the fetching of information from the ROS interface via pointers to variables of...
Definition: API.h:36
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:391
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:829
TInterface::SaveImageNoGridMenuItem
TMenuItem * SaveImageNoGridMenuItem
Definition: InterfaceUnit.h:483
TInterface::SelectMenuItemClick
void __fastcall SelectMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11726
TInterface::PlayerHandshakingActions
void PlayerHandshakingActions()
called in ClockTimer2 if player multiplay in progress
Definition: InterfaceUnit.cpp:29117
TInterface::BypassPDCrossoverMismatch
bool BypassPDCrossoverMismatch(int Caller, int HLoc, int VLoc)
Check for bidir crossover between two single PD lines. Used in PD check function.
Definition: InterfaceUnit.cpp:13451
TRailGraphics::SpeedBut74NormBlackGlyph
Graphics::TBitmap * SpeedBut74NormBlackGlyph
Definition: GraphicUnit.h:1091
TInterface::StartY
int StartY
the mouse position in terms of pixels when an item of text is being selected for moving
Definition: InterfaceUnit.h:1473
FootCrossing
@ FootCrossing
Definition: TrackUnit.h:66
TInterface::TextBoxKeyPress
void __fastcall TextBoxKeyPress(TObject *Sender, char &Key)
Definition: InterfaceUnit.cpp:1230
TInterface::SaveTimetableToSessionFile
bool SaveTimetableToSessionFile(int Caller, std::ofstream &SessionFile, AnsiString SessionFileStr)
Called during a session save to save the current timetable in the session file, true if successful.
Definition: InterfaceUnit.cpp:23971
TInterface::MPHPCancelButton
TButton * MPHPCancelButton
Definition: InterfaceUnit.h:718
TInterface::SpeedButton71
TSpeedButton * SpeedButton71
Definition: InterfaceUnit.h:607
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7596
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:471
TInterface::DeleteAllPrefDirButtonClick
void __fastcall DeleteAllPrefDirButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2140
Platform
@ Platform
Definition: TrackUnit.h:66
TOnePrefDir::SearchVector
TPrefDirVector SearchVector
pref dir vectors, first is the main vector, second used to store search elements temporarily
Definition: TrackUnit.h:1414
TInterface::RlyFile
bool RlyFile
indicates that a loaded railway file is ready for operation, i.e. is a valid .rly file
Definition: InterfaceUnit.h:1336
TInterface::RestartSessionOperMode
@ RestartSessionOperMode
Definition: InterfaceUnit.h:1016
TInterface::PreferredRouteFlag
bool PreferredRouteFlag
used to select either ConvertAndAddPreferredRouteSearchVector or ConvertAndAddNonPreferredRouteSearch...
Definition: InterfaceUnit.h:1330
TTrack::SpeedHeatMapFlag
bool SpeedHeatMapFlag
true when plotting a speed heatmap
Definition: TrackUnit.h:765
TInterface::SkipTTActionsListBoxMouseUp
void __fastcall SkipTTActionsListBoxMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
Definition: InterfaceUnit.cpp:14432
TTrack::LevelCrossingBarrierDownFlashDuration
float LevelCrossingBarrierDownFlashDuration
duration of the flash period when level crossing opening
Definition: TrackUnit.h:783
TInterface::TextMoveHPos
int TextMoveHPos
Definition: InterfaceUnit.h:1485
TTrack::LocationsNotNamed
bool LocationsNotNamed(int Caller)
True if there are unnamed NamedLocationElements (includes footcrossings)
Definition: TrackUnit.cpp:4702
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:755
TInterface::PowerTopLabel
TLabel * PowerTopLabel
Definition: InterfaceUnit.h:228
TInterface::ReminderHeaderPanel
TPanel * ReminderHeaderPanel
Definition: InterfaceUnit.h:763
TTrack::RotLeftArray
int RotLeftArray[FirstUnusedSpeedTagNumber]
holds TrackElement SpeedTag values for 'rotating left' via menu items 'Edit' & 'Rotate left'
Definition: TrackUnit.h:793
TInterface::SpeedButton129
TSpeedButton * SpeedButton129
Definition: InterfaceUnit.h:665
TInterface::RepairFailedTrainMenuItem
TMenuItem * RepairFailedTrainMenuItem
Definition: InterfaceUnit.h:511
TUtilities::CheckAndCompareFileString
bool CheckAndCompareFileString(std::ifstream &InFile, AnsiString InString)
checks that the value is a string ('0' or CR accepted as delimiters) and is the same as InString,...
Definition: Utilities.cpp:498
TInterface::StaticFeaturesScreen
TImage * StaticFeaturesScreen
basic features only for replacing long service references
Definition: InterfaceUnit.h:1511
TTrack::RepairFailedPoints
void RepairFailedPoints(TFailedElementVector::iterator FPVIt)
restore points to unfailed state, added at v2.13.0
Definition: TrackUnit.cpp:12429
TInterface::CutTTEntryButton
TButton * CutTTEntryButton
Definition: InterfaceUnit.h:171
TInterface::AZOrderKeyFlag
bool AZOrderKeyFlag
Definition: InterfaceUnit.h:1409
TInterface::MPCPLabel7
TLabel * MPCPLabel7
Definition: InterfaceUnit.h:728
TInterface::MileEdit
TEdit * MileEdit
Definition: InterfaceUnit.h:138
TOnePrefDir
The basic preferred direction class, consisting of any number of elements with preferred directions s...
Definition: TrackUnit.h:1338
TInterface::SpeedHeatMapImage
TImage * SpeedHeatMapImage
Definition: InterfaceUnit.h:312
TInterface::TTClockx8ButtonClick
void __fastcall TTClockx8ButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16857
TInterface::ADFTop
int ADFTop
stores the ADForm position for re-use when made visible.
Definition: InterfaceUnit.h:1036
TInterface::SetLevel2OperMode
void SetLevel2OperMode(int Caller)
Sets the Level2OperMode user mode, using the Level2OperMode variable to determine the mode.
Definition: InterfaceUnit.cpp:19926
TTrack::NonFootCrossingNamedLocationExists
bool NonFootCrossingNamedLocationExists(int Caller)
True if there is a platform, NamedNonStationLocation or Concourse present in the railway.
Definition: TrackUnit.cpp:9801
TInterface::RestoreTTButton
TButton * RestoreTTButton
Definition: InterfaceUnit.h:187
TInterface::SaveConfigFile
void SaveConfigFile(int Caller)
Save Config.txt file to disk when program ends.
Definition: InterfaceUnit.cpp:17831
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:427
TGraphicElement::SetScreenHVSource
void SetScreenHVSource(int Caller, int HPosIn, int VPosIn)
Set HPos, VPos & SourceRect member values from the supplied positions.
Definition: TrackUnit.cpp:1815
TInterface::EraseLocationNameText
bool EraseLocationNameText(int Caller, AnsiString Name, int &HPos, int &VPos)
Erase a location name (providing it exists in LocationNameMultiMap) from TextVector,...
Definition: InterfaceUnit.cpp:26219
TInterface::PrefDirKey
TImage * PrefDirKey
information panel displayed when setting preferred directions
Definition: InterfaceUnit.h:318
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:111
TOneRoute::GetNextNonPreferredRouteElement
bool GetNextNonPreferredRouteElement(int Caller, int HLoc, int VLoc, bool Callon, IDInt &ReqPosRouteID, bool &PointsChanged)
Try to find a set of linked tracks between the route start element and the one at HLoc & VLoc....
Definition: TrackUnit.cpp:17212
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:125
TInterface::SpeedButton138
TSpeedButton * SpeedButton138
Definition: InterfaceUnit.h:674
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:7151
TTrack::UserGraphicVector
TUserGraphicVector UserGraphicVector
Definition: TrackUnit.h:823
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TInterface::DeleteAllPrefDirButton
TBitBtn * DeleteAllPrefDirButton
Definition: InterfaceUnit.h:158
TInterface::SpeedButton117
TSpeedButton * SpeedButton117
Definition: InterfaceUnit.h:653
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:419
TInterface::SelectPrefDir
TOnePrefDir * SelectPrefDir
Pref Dir elements in a selected region.
Definition: InterfaceUnit.h:1518
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:169
TInterface::SpeedButton39
TSpeedButton * SpeedButton39
Definition: InterfaceUnit.h:575
TTrack::BuildBasicElementFromSpeedTag
TTrackElement BuildBasicElementFromSpeedTag(int Caller, int SpeedTag)
Return a basic track element from the SpeedTag new at v2.2.0 - needed because Interface doesn't have ...
Definition: TrackUnit.h:935
TInterface::TTStartTimeBox
TEdit * TTStartTimeBox
edit box that displays the timetable start time
Definition: InterfaceUnit.h:211
TInterface::AddPrefDirButtonClick
void __fastcall AddPrefDirButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2111
TUtilities::NilPointChangeEventsPerFailure
int NilPointChangeEventsPerFailure
Definition: Utilities.h:57
TInterface::ShiftKey
bool ShiftKey
true when the SHIFT key is pressed (see also CtrlKey)
Definition: InterfaceUnit.h:1350
TInterface::MPCPLabel3
TLabel * MPCPLabel3
Definition: InterfaceUnit.h:710
TInterface::MPHPLoadCouplingFileButton
TButton * MPHPLoadCouplingFileButton
Definition: InterfaceUnit.h:719
TTrainController::SigSLow
bool SigSLow
Definition: TrainUnit.h:860
TTrack::TActiveLevelCrossing
Definition: TrackUnit.h:618
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6620
TInterface::AddMinsButtonClick
void __fastcall AddMinsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3863
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:56
TTrack::SaveSessionBarriersDownVector
void SaveSessionBarriersDownVector(int Caller, std::ofstream &OutFile)
Save all vector values to the session file.
Definition: TrackUnit.cpp:3678
TInterface::HostHandshakingActions
void HostHandshakingActions()
called in ClockTimer2 if host multiplay in progress
Definition: InterfaceUnit.cpp:27857
TInterface::FontButtonClick
void __fastcall FontButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2036
TInterface::AddTimeMinutes
AnsiString AddTimeMinutes(AnsiString OrigTimeString, int MinsToAdd)
used in ExpandRepeatsButtonClick function to add minutes to an AnsiString time
Definition: InterfaceUnit.cpp:6274
TDisplay::PlotAndAddUserGraphic
void PlotAndAddUserGraphic(int Caller, TUserGraphicItem UserGraphicItem)
Plot user graphic.
Definition: DisplayUnit.cpp:101
TInterface::PlayerAwaitingHostStartFlag
bool PlayerAwaitingHostStartFlag
Definition: InterfaceUnit.h:1205
TInterface::SigAspectButtonClick
void __fastcall SigAspectButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1997
TInterface::SetLevel2TrackMode
void SetLevel2TrackMode(int Caller)
Sets the Level2TrackMode user mode, using the Level2TrackMode variable to determine the mode.
Definition: InterfaceUnit.cpp:19124
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:22409
TInterface::TwoLocationNameButtonClick
void __fastcall TwoLocationNameButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:17454
TInterface::None
@ None
Definition: InterfaceUnit.h:1071
TInterface::SpeedButton87
TSpeedButton * SpeedButton87
Definition: InterfaceUnit.h:623
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:115
TInterface::FormClose
void __fastcall FormClose(TObject *Sender, TCloseAction &Action)
Definition: InterfaceUnit.cpp:15567
TInterface::SigAspectButton
TBitBtn * SigAspectButton
Definition: InterfaceUnit.h:113
THVShortPair
std::pair< int, int > THVShortPair
Definition: InterfaceUnit.h:82
TTrack::SelectVectorAt
TTrackElement & SelectVectorAt(int Caller, int At)
A range-checked version of SelectVector.at(At)
Definition: TrackUnit.cpp:12000
TTrack::LCChangeFlag
bool LCChangeFlag
true when LCs changing
Definition: TrackUnit.h:759
TInterface::PreviousTTEntryKeyFlag
bool PreviousTTEntryKeyFlag
Definition: InterfaceUnit.h:1399
TInterface::CPEditDepRange
TEdit * CPEditDepRange
Definition: InterfaceUnit.h:286
TInterface::SpeedButton127
TSpeedButton * SpeedButton127
Definition: InterfaceUnit.h:663
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:875
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:926
TInterface::SelectBitmapMouseLocY
int SelectBitmapMouseLocY
see above
Definition: InterfaceUnit.h:1460
TInterface::RemovePlayerFromStringGridAndInfoVector
void RemovePlayerFromStringGridAndInfoVector(int Caller, AnsiString PlayerUserName)
utility to clear user name from string grid & infovector (used several times)
Definition: InterfaceUnit.cpp:29069
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5682
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:451
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:91
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
THVPair
std::pair< int, int > THVPair
HLoc/VLoc position pair.
Definition: TrackUnit.h:41
TPrefDirElement::SetCheckCount
void SetCheckCount(int ChkCnt)
Definition: TrackUnit.h:334
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:757
TTrack::TActiveLevelCrossing::ChangeDuration
float ChangeDuration
duration of the level crossing changing period
Definition: TrackUnit.h:627
TActionsDueForm::ActionsDueListBoxRightMouseButtonDown
bool ActionsDueListBoxRightMouseButtonDown
Definition: ActionsDueUnit.h:30
TDisplay::DisplayOffsetVHome
static int DisplayOffsetVHome
the vertical offset of the 'Home' display
Definition: DisplayUnit.h:83
TInterface::StartX
int StartX
Definition: InterfaceUnit.h:1473
TrainUnit.h
TTrack::LevelCrossingBarrierUpFlashDuration
float LevelCrossingBarrierUpFlashDuration
duration of the flash period when level crossing closing to trains
Definition: TrackUnit.h:781
PassTime
@ PassTime
Definition: TrainUnit.h:71
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:888
TUtilities::SetLocaleResultOK
bool SetLocaleResultOK
flag to indicate whether the call to setlocale() in InterfaceUnit.cpp succeeded or not
Definition: Utilities.h:81
TInterface::SelectLengthsFlag
bool SelectLengthsFlag
true when 'Set lengths &/or speeds' selected in the 'Edit' menu
Definition: InterfaceUnit.h:1346
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag = 1, top = 2, top rh diag = 3,...
Definition: TrackUnit.h:90
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:687
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1077
TInterface::MultiplayerMenu
TMenuItem * MultiplayerMenu
Definition: InterfaceUnit.h:696
TTextItem::TextString
AnsiString TextString
the text string
Definition: TextUnit.h:47
TInterface::TrackOKButton
TBitBtn * TrackOKButton
Definition: InterfaceUnit.h:103
TInterface::SpeedButton23
TSpeedButton * SpeedButton23
Definition: InterfaceUnit.h:559
TInterface::RailwayTitle
AnsiString RailwayTitle
Definition: InterfaceUnit.h:1262
TInterface::TNumHVPair
std::pair< unsigned char, THVShortPair > TNumHVPair
Definition: InterfaceUnit.h:1133
TDisplay::Rectangle
void Rectangle(int Caller, int HPos, int VPos, TColor Col, int Size, int Width)
Plot a rectangle at the defined position with colour Col & size defined by Size.
Definition: DisplayUnit.cpp:156
TInterface::SpeedButton20
TSpeedButton * SpeedButton20
Definition: InterfaceUnit.h:556
API.h
File containing classes and methods for broadcasting ROS status data.
TInterface::ModerateFailuresMenuItemClick
void __fastcall ModerateFailuresMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27062
TInterface::CheckInterface
bool CheckInterface(int Caller, std::ifstream &SessionFile)
Check the interface part of a session file & return false for error, called during SessionFileIntegri...
Definition: InterfaceUnit.cpp:23739
TInterface::PreStart
@ PreStart
Definition: InterfaceUnit.h:1055
TRailGraphics::SolidCircleGreen
Graphics::TBitmap * SolidCircleGreen
Definition: GraphicUnit.h:567
AboutForm
TAboutForm * AboutForm
Definition: AboutUnit.cpp:47
TInterface::SpeedButton137
TSpeedButton * SpeedButton137
Definition: InterfaceUnit.h:673
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:155
TInterface::ExitTTModeButton
TBitBtn * ExitTTModeButton
Definition: InterfaceUnit.h:165
TInterface::TrainLongServRefInfoOnOffMenuItemClick
void __fastcall TrainLongServRefInfoOnOffMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:29613
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:4100
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning and perf log panels when the action is reached
Definition: TrainUnit.h:131
TInterface::OverallSpeedLimit
int OverallSpeedLimit
and the overall speed limit, if the speed limits vary across the selection the value is set to -1
Definition: InterfaceUnit.h:1446
TOnePrefDir::GetFixedSearchElementAt
const TPrefDirElement & GetFixedSearchElementAt(int Caller, int At) const
Return a non-modifiable element at SearchVector position 'At'.
Definition: TrackUnit.cpp:12571
TInterface::SpeedButton78
TSpeedButton * SpeedButton78
Definition: InterfaceUnit.h:614
TRailGraphics::SpeedBut75GrndBlackGlyph
Graphics::TBitmap * SpeedBut75GrndBlackGlyph
Definition: GraphicUnit.h:1100
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:217
TInterface::PowerEditBox
TEdit * PowerEditBox
Definition: InterfaceUnit.h:227
TInterface::TimetableHandler
void TimetableHandler()
Called during timetable editing whenever a change is made to the timetable, sets all the timetable bu...
Definition: InterfaceUnit.cpp:7004
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7431
TTextHandler::TextErase
bool TextErase(int Caller, int HPosition, int VPosition, AnsiString TextToErase)
Definition: TextUnit.cpp:269
TUtilities::DefaultTrackSpeedLimit
int DefaultTrackSpeedLimit
speed limit of each track element before being changed within the program (can be changed in config....
Definition: Utilities.h:97
TInterface::CancelSelectionMenuItem
TMenuItem * CancelSelectionMenuItem
Definition: InterfaceUnit.h:475
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:354
TInterface::SpeedButton68
TSpeedButton * SpeedButton68
Definition: InterfaceUnit.h:604
TInterface::HostInSessionFlag
bool HostInSessionFlag
Definition: InterfaceUnit.h:1199
TInterface::LoadSession
void LoadSession(int Caller)
Load a session file.
Definition: InterfaceUnit.cpp:22670
TAllRoutes::SetTrailingSignalsOnContinuationRoute
void SetTrailingSignalsOnContinuationRoute(int Caller, int RouteNumber, int AccessNumber)
This is called by the InterfaceUnit at intervals based on entries in the ContinuationAutoSigVector in...
Definition: TrackUnit.cpp:20888
TDisplay::DisplayZoomOutOffsetHHome
static int DisplayZoomOutOffsetHHome
the horizontal offset of the zoomed-out 'Home' display
Definition: DisplayUnit.h:89
TInterface::SigRouteStartMarker
TGraphicElement * SigRouteStartMarker
Definition: InterfaceUnit.h:1503
TInterface::SetGapsButtonClick
void __fastcall SetGapsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:1168
TInterface::SignallerControlStopMenuItemClick
void __fastcall SignallerControlStopMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:15197
TInterface::LengthHeatMapImage
TImage * LengthHeatMapImage
Definition: InterfaceUnit.h:311
TInterface::PrefDirSelecting
@ PrefDirSelecting
Definition: InterfaceUnit.h:1060
TInterface::TimetableDialog
TOpenDialog * TimetableDialog
Definition: InterfaceUnit.h:522
TTrack::FindHighestLowestAndLeftmostNamedElements
bool FindHighestLowestAndLeftmostNamedElements(int Caller, AnsiString Name, int &VPosHi, int &VPosLo, int &HPos)
Used in locating the screen name position for a named location, return true if find an inactive eleme...
Definition: TrackUnit.cpp:12029
TInterface::MoveTTEntryUpButton
TButton * MoveTTEntryUpButton
Definition: InterfaceUnit.h:174
TInterface::SigPrefConsecButton
TBitBtn * SigPrefConsecButton
Definition: InterfaceUnit.h:236
TInterface::TTCurrentEntryIterator
TTEVIterator TTCurrentEntryIterator
Definition: InterfaceUnit.h:1529
TInterface::PlayerReadyToBeginFlag
bool PlayerReadyToBeginFlag
Definition: InterfaceUnit.h:1203
TInterface::MPCPReadyToBeginButtonClick
void __fastcall MPCPReadyToBeginButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:28321
FinishSequence
@ FinishSequence
Definition: TrainUnit.h:81
TInterface::RotateMenuItem
TMenuItem * RotateMenuItem
Definition: InterfaceUnit.h:470
TInterface::SpeedButton33
TSpeedButton * SpeedButton33
Definition: InterfaceUnit.h:569
TInterface::MultiplayerPlayerPanel
TPanel * MultiplayerPlayerPanel
Definition: InterfaceUnit.h:705
TInterface::TTClockxSixteenthButtonClick
void __fastcall TTClockxSixteenthButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16971
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:678
TTextHandler::TextVectorSize
unsigned int TextVectorSize(int Caller)
return the number of items in TextVector
Definition: TextUnit.cpp:536
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:75
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int TrackPos, int LinkPos, int OwnTrainID)
True if another train on LinkPos track of element at TrackPos, whether bridge or not,...
Definition: TrackUnit.cpp:11967
TInterface::FailureMenu
TMenuItem * FailureMenu
Definition: InterfaceUnit.h:750
TInterface::FlashControlButton
TBitBtn * FlashControlButton
Definition: InterfaceUnit.h:758
TInterface::FlipMenuItem
TMenuItem * FlipMenuItem
Definition: InterfaceUnit.h:468
TTrack::SetLinkedLevelCrossingBarrierAttributes
void SetLinkedLevelCrossingBarrierAttributes(int Caller, int HLoc, int VLoc, int Attr)
Set linked LC attributes; 0=closed to trains, 1 = open to trains, 2 = changing state = closed to trai...
Definition: TrackUnit.cpp:6521
TInterface::MinorFailuresMenuItem
TMenuItem * MinorFailuresMenuItem
Definition: InterfaceUnit.h:752
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:161
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:67
TInterface::SessionFileIntegrityCheck
bool SessionFileIntegrityCheck(int Caller, AnsiString FileName)
Checks session file integrity prior to loading, true for success.
Definition: InterfaceUnit.cpp:24531
TInterface::TrainFailedImage
TImage * TrainFailedImage
Definition: InterfaceUnit.h:307
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:514
TInterface::RotLeftMenuItemClick
void __fastcall RotLeftMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:12525
TInterface::SpeedButton14
TSpeedButton * SpeedButton14
Definition: InterfaceUnit.h:550
TInterface::ExitTrackButton
TBitBtn * ExitTrackButton
Definition: InterfaceUnit.h:114
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:751
TInterface::SpeedButton110
TSpeedButton * SpeedButton110
Definition: InterfaceUnit.h:646
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TInterface::OutputLog10
TLabel * OutputLog10
Definition: InterfaceUnit.h:349
TInterface::SESSION_DIR_NAME
static const UnicodeString SESSION_DIR_NAME
Definition: InterfaceUnit.h:1048
TInterface::PasteTTEntryKeyFlag
bool PasteTTEntryKeyFlag
Definition: InterfaceUnit.h:1406
TInterface::FileChangedFlag
bool FileChangedFlag
true when a loaded railway file has changed (used to warn user if opts to exit without saving)
Definition: InterfaceUnit.h:1298
TInterface::TooShortMessageSentFlag
bool TooShortMessageSentFlag
indicates that the length of a location element might be too short (<50m), so it won't be given again
Definition: InterfaceUnit.h:1372
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
TInterface::CutTTEntryButtonClick
void __fastcall CutTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4459
TInterface::AutoSigsButtonClick
void __fastcall AutoSigsButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:2262
TRailGraphics::SpeedBut69GrndBlackGlyph
Graphics::TBitmap * SpeedBut69GrndBlackGlyph
Definition: GraphicUnit.h:1094
Signaller
@ Signaller
Definition: TrainUnit.h:64
TRailGraphics::bmRedRect
Graphics::TBitmap * bmRedRect
Definition: GraphicUnit.h:531
TInterface::SaveOperatingImageMenuItemClick
void __fastcall SaveOperatingImageMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:3156
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:907
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TUtilities::MajorSignalChangeEventsPerFailure
int MajorSignalChangeEventsPerFailure
Definition: Utilities.h:67
TInterface::ConstructRoute
TOneRoute * ConstructRoute
the route under construction
Definition: InterfaceUnit.h:1520
TInterface::SpeedButton118
TSpeedButton * SpeedButton118
Definition: InterfaceUnit.h:654
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:895
TTrack::PlotSmallRedGap
void PlotSmallRedGap(int Caller)
Plot on screen in zoomed-out mode and in gap setting mode a small red square corresponding to the gap...
Definition: TrackUnit.cpp:10995
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:431
Bridge
@ Bridge
Definition: TrackUnit.h:66
TInterface::MinorFailuresMenuItemClick
void __fastcall MinorFailuresMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:27046
TInterface::DirOpenError
bool DirOpenError
true when one of the program subfolders doesn't already exist and can't be created
Definition: InterfaceUnit.h:1292
TInterface::CutMenuItemClick
void __fastcall CutMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11819
FMajor
@ FMajor
Definition: Utilities.h:43
TInterface::InvertTTEntryButtonClick
void __fastcall InvertTTEntryButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:4068
TInterface::SpeedButton38
TSpeedButton * SpeedButton38
Definition: InterfaceUnit.h:574
TInterface::ShowHideStringGridMenuItemClick
void __fastcall ShowHideStringGridMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:29100
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:80
TInterface::ResetCurrentSpeedButton
void ResetCurrentSpeedButton(int Caller)
Resets the CurrentSpeedButton variable to zero and the 'Down' property to false.
Definition: InterfaceUnit.cpp:18365
TInterface::SpeedButton102
TSpeedButton * SpeedButton102
Definition: InterfaceUnit.h:638
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:443
TInterface::SpeedButton99
TSpeedButton * SpeedButton99
Definition: InterfaceUnit.h:635
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:37
TInterface::CPDeparturesCheckBox
TCheckBox * CPDeparturesCheckBox
Definition: InterfaceUnit.h:283
Buffers
@ Buffers
Definition: TrackUnit.h:66
TInterface::SelectGraphic
@ SelectGraphic
Definition: InterfaceUnit.h:1065
TTextHandler::TextMove
void TextMove(int Caller, int HPosInput, int VPosInput, int &TextItem, int &TextMoveHPos, int &TextMoveVPos, bool &TextFoundFlag)
Definition: TextUnit.cpp:212
TUtilities::ScreenElementHeight
int ScreenElementHeight
height of display screen in elements
Definition: Utilities.h:105
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:297
TInterface::TTFirstServiceIterator
TTEVIterator TTFirstServiceIterator
Definition: InterfaceUnit.h:1529
TTrack::UserGraphicPresentAtHV
bool UserGraphicPresentAtHV(int Caller, int HPos, int VPos, int &UGIVectorPos)
checks if a user graphic present
Definition: TrackUnit.h:857
TOnePrefDir::GetPrefDirStartElement
bool GetPrefDirStartElement(int Caller, int HLoc, int VLoc)
Used when beginning a chain of preferred directions or element lengths. Enter with HLoc & VLoc set to...
Definition: TrackUnit.cpp:12595
TInterface::ExitTTModeButtonClick
void __fastcall ExitTTModeButtonClick(TObject *Sender)
Definition: InterfaceUnit.cpp:5439
TInterface::MirrorMenuItemClick
void __fastcall MirrorMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:11979
TInterface::SpeedButton11
TSpeedButton * SpeedButton11
Definition: InterfaceUnit.h:547
TInterface::ReminderExitImage
TImage * ReminderExitImage
Definition: InterfaceUnit.h:764
TInterface::RouteNotStarted
@ RouteNotStarted
Definition: InterfaceUnit.h:1071
TTrack::GapsUnset
bool GapsUnset(int Caller)
True if there are gaps in the railway and any are unset.
Definition: TrackUnit.cpp:4614
TInterface::SaveImageAndGridMenuItem
TMenuItem * SaveImageAndGridMenuItem
Definition: InterfaceUnit.h:484
TInterface::SpeedButton48
TSpeedButton * SpeedButton48
Definition: InterfaceUnit.h:584
TInterface::WhiteBgndMenuItemClick
void __fastcall WhiteBgndMenuItemClick(TObject *Sender)
Definition: InterfaceUnit.cpp:16340